MyBatis学习一

一、框架介绍

  1. 持久层框架:MyBatis、Hibernate、Spring Data、iBatis
  2. MVC框架:Spring MVC、Struts1、Struts2
  3. 项目管理框架:Spring Framework、Spring Boot
  4. 微服务框架:Spring Cloud
  5. 权限管理框架:Sping Security、Shiro

常见组合:

  1. SSM:Spring Framework + Spring MVC + MyBatis。最常见组合,属于Java程序员必须掌握的内容。
  2. SSMP:Spring Framework + Spring MVC + MyBatis+MyBatis Plus。对SSM的增强,减少SQL的编写。
  3. Spring Boot + Spring Cloud。微服务架构项目常用组合。
  4. Spring Boot。新项目最先选择,比SSM组合使用起来更加方便。
  5. Spring Boot + Shiro/Spring Security。具有权限控制时的组合。
  6. SSM + Shiro/Spring Security。具有权限控制时的组合。

二、MyBatis 介绍

2.1 什么是 MyBatis

MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 几乎消除了 JDBC 代码和参数的手动设置以及 结果集的检索。MyBatis 使用简单的 XML 或注解进行配置,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射到数据库中的记录。

与其他 ORM 框架不同,MyBatis 并没有将 Java 对象与数据库表关联起来,而是将方法与 SQL 语句关联。MyBatis 让开发人员可以使用更自然的面向对象的方式来操作数据库。

2.2 MyBatis 解决的问题

  1. 简化 JDBC 操作:MyBatis 简化了 JDBC 操作,减少手动编写 JDBC 代码的工作量。
  2. SQL 与代码分离:MyBatis 将 SQL 语句与 Java 代码分离,便于维护和管理。
  3. 参数映射:自动将 Java 对象映射到 SQL 语句的参数中。
  4. 结果映射:自动将 SQL 查询结果映射为 Java 对象。
  5. 支持动态 SQL:可以根据不同的条件动态生成 SQL 语句。
  6. 缓存机制:提供一级缓存和二级缓存,提高查询效率。

2.3 ORM

ORM(Object/Relation Mapping),中文名称:对象/关系 映射。是一种解决数据库发展和面向对象编程语言发展不匹配问题而出现的技术。

在 ORM 中强调以下几点:

  • ORM 技术具体实现可以认为是一个整体。
  • 程序员在使用 ORM 时,只需要把对象交给 ORM,不需要去编写卸载对象的代码,而是由 ORM 把对象中属性值取出放入到 SQL 中。
  • SQL 执行结果后,如果执行的是查询,会由 ORM 把从数据库查询到的结果,转为对象。程序员从 ORM 获取到的就是转换后的对象。所以 ORM 技术相当于一个转换器,是面向对象语言和数据库之间的一个纽带。
    在这里插入图片描述

2.4 与 JDBC、Hibernate 的对比

特性JDBCMyBatisHibernate
开发效率
灵活性
学习曲线陡峭平缓较陡峭
SQL控制完全手动自定义自动生成
性能依赖优化
映射关系SQL映射对象-关系映射

三、创建一个 MyBatis 项目

3.1 创建数据库和表

在本机 MySQL 中创建数据库,示例数据库叫做 bjsxt

在 bjsxt 中创建表 flower。示例如下

-- 创建数据库
CREATE DATABASE `bjsxt` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_bin';

-- 创建表
CREATE TABLE `flower` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `price` double(10,2) DEFAULT NULL,
  `production` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

3.2 创建 Maven 项目并添加依赖

创建一个 Maven 项目(示例:mybatis1),并配置 pom.xml

需要添加 MyBatis 的依赖和 MySQL驱动依赖。

提示:

1.MyBatis 框架只有一个依赖,但不要认为所有框架都是一个依赖。以后的框架可能需要很多依赖。
2.驱动包需要自己导入的,MyBatis 并没有依赖数据库驱动,因为 MyBatis 研发者并不知道后使用这个框架的人使用了哪款数据库
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bjsxt</groupId>
    <artifactId>mybatis1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- MyBatis 框架依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!-- log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!--资源拷贝插件-->
    <build>
        <resources>
            <resource>
                <!-- 希望编译的目录 -->
                <directory>src/main/java</directory>
                <!-- Maven在这个目录原有功能不变,额外再添加其他的类型 -->
                <includes>
                    <!-- 告诉Maven还需要编译什么类型的文件 -->
                    <include>**/*.xml</include>
                </includes>
                <!-- 是否在编译文件过程中,对文件内容进行过滤处理.是否对文件中内容进行操作 -->
                <filtering>true</filtering>
            </resource>
            <!-- 如果不配置,resources中内容不再进行编译 -->
            <!-- 只要配置上,把原有约定生效-->
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

3.3 MyBatis 属性加载

MyBatis 支持加载属性文件(.properties 文件),可以通过在属性文件中配置数据库连接属性然后加载。这种方式要比直接写稍微麻烦一点点,但是却把所有的数据库连接书写到统一文件中,方便后续查看和修改。

src/main/resources 目录中创建 jdbc.properties 文件

m_url=jdbc:mysql://localhost:3306/bjsxt?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
m_driver=com.mysql.cj.jdbc.Driver
m_username=root
m_password=123456

3.4 MyBatis 启用日志功能

MyBatis框架内置日志工厂。日志工厂负责自动加载项目中配置的日志。MyBatis支持以下日志,当存在多个日志工具时,严格按照从上往下顺序使用,且只会使用一个。

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j (deprecated since 3.5.9)
  • JDK logging

其中Log4j是MyBatis中之前使用较多的一个日志实现。但是从3.5.9版本被替换了,在目前3.5.9版本中还能使用,但是在将来的版本中会被移除。下面分别演示MyBatis整合Log4j和Log4j的实现方案

3.4.1 在 pom.xml 中配置 依赖

 <!-- log4j -->
 <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
 </dependency>

3.4.2 创建 Log4j 配置文件

在 resources 中创建 log4j.properties。名称必须叫这个名字,扩展名必须是.properties。

在配置文件中填写下面内容

log4j.rootLogger=ERROR, stdout
# log4j.logger?????a.b.c????????????????
log4j.logger.com.bjsxt.mapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

3.5 创建 MyBatis 全局配置文件

在项目的 src/main/resources 目录下新建 mybatis.xml 配置文件。并在配置文件中填写下面内容

提示:

1. 文件名称为MyBatis官方示例时配置文件名。平时所说的规范:官方怎么写,那就是规范。所以从规范角度上我们也叫mybatis.cfg.xml。但要知道,这个文件的名称是自定义的。叫其他名称也不影响使用。
2. 无论如何封装JDBC,数据库连接的四个参数是无法封装的。如果一个框架可以自动获取本地数据库账号和密码,那就别叫框架了,叫木马更合适。
3. 框架学习过程中配置文件绝大多数都是XML类型。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 读取属性文件-->
    <properties resource="jdbc.properties"></properties>

    <!--指定当前日志的类型-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <!-- 开启驼峰转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <typeAliases>
        <!-- 给包中所有的类起别名 别名就是类名 不区分大小写-->
        <package name="com.bjsxt.pojo"/>
    </typeAliases>
    <!-- 配置连接数据库环境-->
    <environments default="mysql">
        <!--mysql-->
        <environment id="mysql">
            <!--配置事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="${m_driver}"/>
                <property name="url"
                          value="${m_url}"/>
                <property name="username" value="${m_username}"/>
                <property name="password" value="${m_password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--解析FlowerMapper.xml-->
    <mappers>
        <!-- 指定目录中文件-->
        <!--<mapper resource="mapper/FlowerMapper.xml"></mapper>-->
        <!--
            如果采用的是包扫描
                A、接口必须和 xml 在同一个包中
                B、接口的名称必须和 xml 的名称保持一致
         -->
        <package name="com.bjsxt.mapper"/>
    </mappers>
</configuration>

配置文件解释:

  1. XML 文档头是 XML 文件必须有的
  2. DOCTYPE 最好有,不写没有提示
  3. <configuration> 是根节点,没有什么含义
  4. <environments> 的 default 属性取值必须是下面某个 <environment 标签的 id 属性值。表示当前 MyBatis 框架到底使用哪个环境。
  5. <environment 的 id 值是自定义的。表示当前数据库环境的名称。在全局配置文件中 <environment 标签可以有多个,里面分别配置上数据源相关参数。例如一个里面的配置连接本地 MySQL 的参数,另一个配置连接服务器中 MySQL 的参数,还有一个配置连接 Oracle 数据库的相关参数。都准备好了以后可以通过修改 <environments 中 default 的值来切换连接的数据库,这样要比修改里面具体的连接参数更加方便。
  6. <transactionManager> 中 type 属性用于配置事务的类型。可取值:
    • JDBC:使用原生 JDBC 的事务管理进行提交和回滚。
    • MANAGED:MyBatis不参与事务管理。交给其他人管理事务
  7. <dataSource 中 type 属性值用于配置数据源类型。可取值:
    • UNPOOLED:不使用数据库连接池。每次执行SQL都会打开数据库连接,执行SQL结束都会关闭连接。适用于简单小型应用。
    • POOLED:使用MyBatis内置数据库连接池。对于频繁访问数据库的应用程序来说,这个取值是很适合的。
    • JNDI:使用本地目录接口技术连接容器外部的数据源。这种方式很少使用,一般都是极其复杂的项目,对数据源要求极高的情况下才能使用。
  8. <property> 配置具体属性和值
  9. driver、url、username、password四个属性名称是固定的,但是没有顺序要求,添加到<properties>的name属性中即可。分别代表驱动类、连接字符串、用户名、密码。value属性的值为连接数据库的具体参数值。

3.6 创建 Mapper 类

在 com/bjsxt 下创建 mapper 目录,在这里面 创建一个 一个接口 文件 FlowerMapper.class 文件,文件内容如下:

package com.bjsxt.mapper;

import com.bjsxt.pojo.Flower;

import java.util.List;

public interface FlowerMapper {

    List<Flower> selectAll();
}

3.7 创建 Mapper 映射文件

在 com/bjsxt/mapper 目录下创建 mapper 映射文件 FlowerMapper.xml 文件,文件内容如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.FlowerMapper">

    <select id="selectAll" resultType="Flower">
        select * from flower
    </select>
</mapper>

提示:

namespace 是和接口文件绑定

<select> 是查询标签, id 属性值是和接口 定义的一致,resultType 是指返回数据类型

3.8 编写测试类 TestA.class

在 test 目录下创建 com/bjsxt/TestA.class

package com.bjsxt;

import com.bjsxt.mapper.FlowerMapper;
import com.bjsxt.pojo.Flower;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TestA {

    @Test
    public void test1() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession();
        FlowerMapper flowerMapper = sqlSession.getMapper(FlowerMapper.class);
        List<Flower> list = flowerMapper.selectAll();
        System.out.println(list);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}

输出结果

DEBUG [main] - ==>  Preparing: select * from flower
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: id, name, price, production
TRACE [main] - <==        Row: 1, 桂花, 199.8, 重---北京尚学堂
TRACE [main] - <==        Row: 2, 玫瑰花, 99.0, 北京
TRACE [main] - <==        Row: 3, 兰竹, 100.0, 四川
TRACE [main] - <==        Row: 4, 秋菊, 55.0, 山东
TRACE [main] - <==        Row: 5, 西蓝花, 2.0, 烟台
TRACE [main] - <==        Row: 6, 康乃馨, 99.0, 上海
TRACE [main] - <==        Row: 10, 菊花, 19.9, 添加
TRACE [main] - <==        Row: 11, 菊花-1, 19.9, 添加
TRACE [main] - <==        Row: 12, 菊花-2, 19.9, 添加
TRACE [main] - <==        Row: 13, 菊花-2, 19.9, 添加
DEBUG [main] - <==      Total: 10
[Flower{id=1, name='桂花', price=199.8, production='重---北京尚学堂'}, Flower{id=2, name='玫瑰花', price=99.0, production='北京'}, Flower{id=3, name='兰竹', price=100.0, production='四川'}, Flower{id=4, name='秋菊', price=55.0, production='山东'}, Flower{id=5, name='西蓝花', price=2.0, production='烟台'}, Flower{id=6, name='康乃馨', price=99.0, production='上海'}, Flower{id=10, name='菊花', price=19.9, production='添加'}, Flower{id=11, name='菊花-1', price=19.9, production='添加'}, Flower{id=12, name='菊花-2', price=19.9, production='添加'}, Flower{id=13, name='菊花-2', price=19.9, production='添加'}]

Process finished with exit code 0

3.9 Mapper 项目目录结构

在这里插入图片描述

四、SQL 参数

1、MyBatis 中的占位符

MyBatis 中最常用的占位符为 #{}>,在 MyBatis 的 mapper 文件的 SQL 中使用 #{}。最终解析时会解析成 JDBC 的占位符 ?

2、当参数为简单数据类型

当参数为一个简单数据类型时。例如:八大基本数据类型、String 类型等。可以通过 #{任意内容} 获取到。

提示:

任意内容代表着,随意写,只要不是违法字符就行。

但是要注意不能不写。

2.1 案例示例

查询操作

mapper 接口文件 文件

package com.bjsxt.mapper;

import com.bjsxt.pojo.Flower;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.ResultHandler;

import java.util.List;
import java.util.Map;

public interface FlowerMapper {
    // A、查询全部,返回值为 List
    List<Flower> selectAll();

    // B、单个参数 返回值为单个对象
    Flower selectOne(int id);

    // C、多个参数 返回值为单个对象
    Flower selectOne2(int id, String name);

    // D、对象 返回值为单个对象
    Flower selectOne3(Flower flower);

    // E、集合 返回值为单个对象
    Flower selectOne4(Map<String, Object> map);

    // F、返回值是一个 Map 集合 使用场景: 如果我们想要某一列直接返回对象的时候可以使用selectMap()
    @MapKey("id")
    Map<Integer, Flower> selectMap();

    //G、selectCursor: 特点: 返回值是一个 Cursor 游标对象 使用场景:我们一般不使用 因为游标查询一条一条返回 占用内存效率低 一般在存储过程中会使用
    Cursor<Flower> selectCursor();

    //H、针对没有返回值的
    void selectAdd(ResultHandler<Flower> handler);
}

mapper xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.FlowerMapper">

    <!--A、查询全部
        List<Flower> selectAll();
    -->
    <select id="selectAll" resultType="Flower">
        select * from flower
    </select>

    <!-- B、单个参数
        Flower selectOne(int id);
        参数接受的时候需要使用:#{param1} ... #{param3}
        更多用法:
            [1] 有且只有【一个参数】时候,并且【参数类型是基本类或者常用类型(String)】的时候,接受参数可以任意定义
            [2] parameterType: 这个属性建议省略
            [3] resultType: 返回值类型,决定不可以省略
    -->
    <select id="selectOne" resultType="Flower">
        select * from flower where id = #{param1}
    </select>

    <!--C、多个参数
        Flower selectOne2(int id, String name);
    -->

    <select id="selectOne2" resultType="Flower">
        select * from flower where id = #{param1} and name = #{param2}
    </select>

    <!-- D、对象
        Flower selectOne3(Flower flower);
        注意:
            在对象中取值的时候 直接写对象属性名称就可以 取值顺序 先找属性 get 方法 ===> 在去找对应属性中取值
    -->
    <select id="selectOne3" resultType="Flower">
        select * from flower where id = #{id} and name = #{name}
    </select>

    <!--E、集合
        Flower selectOne4(Map<String, Object> map);
        注意:
            通过 map 中的 key 取值
    -->
    <select id="selectOne4" resultType="Flower">
        select * from flower where id = #{a} and name = #{b}
    </select>

    <!--F、查询返回值为 Map-->
    <select id="selectMap" resultType="Flower">
        select * from flower
    </select>

    <!--GCursor 使用-->
    <select id="selectCursor" resultType="Flower">
        select * from flower
    </select>

    <!--H、针对没有返回值-->
    <select id="selectAdd" resultType="Flower">
        select * from flower
    </select>
</mapper>

注意:

xml 接受的参数名使用 param1、param2、…、param6

TestA 文件

package com.bjsxt;

import com.bjsxt.mapper.FlowerMapper;
import com.bjsxt.mapper.MyResultHandler;
import com.bjsxt.pojo.Flower;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestA {

    @Test
    public void test1() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession();
        FlowerMapper flowerMapper = sqlSession.getMapper(FlowerMapper.class);

        // A、查询全部
        /*List<Flower> list = flowerMapper.selectAll();
        System.out.println(list);*/

        // B、单个参数传递
        /*Flower flower = flowerMapper.selectOne(1);
        System.out.println(flower);*/

        // C、多个参数传递
        /*Flower flower = flowerMapper.selectOne2(1, "桂花");
        System.out.println(flower);*/

        // D、传递对象
        /*Flower flower = new Flower();
        flower.setId(1);
        flower.setName("桂花");
        Flower flower1 = flowerMapper.selectOne3(flower);
        System.out.println(flower1);*/

        // E、传递集合
        /*HashMap<String, Object> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", "桂花");
        Flower flower = flowerMapper.selectOne4(map);
        System.out.println(flower);*/

        // F、查询返回值为 Map
        /*Map<Integer, Flower> flowerMap = flowerMapper.selectMap();
        System.out.println(flowerMap);*/

        // G、selectCursor: 特点: 返回值是一个 Cursor 游标对象 使用场景:我们一般不使用 因为游标查询一条一条返回
        // 占用内存效率低 一般在存储过程中会使用
        /*Cursor<Flower> cursor =flowerMapper.selectCursor();
        cursor.forEach((t) -> {
            System.out.println(t);
        });*/

        // H、没有返回值,使用场景:当我们想要对查询结果进行增强的时候,但又不更改数据库中内容
        MyResultHandler myResultHandler = new MyResultHandler();
        flowerMapper.selectAdd(myResultHandler);
        List<Flower> list = myResultHandler.getList();
        System.out.println(list);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}

针对没有返回值,我们想对数据预处理的 F

MyResultHandler 文件

package com.bjsxt.mapper;

import com.bjsxt.pojo.Flower;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

import java.util.ArrayList;
import java.util.List;

public class MyResultHandler implements ResultHandler<Flower> {

    private List<Flower> list = new ArrayList<>();
    @Override
    public void handleResult(ResultContext<? extends Flower> resultContext) {
        // 每一条查询操作都会进入当前方法
        Flower flower = resultContext.getResultObject();
        flower.setProduction(flower.getProduction() + "北京");
        list.add(flower);
    }

    public List<Flower> getList() {
        return list;
    }
}
分页操作

mapper 接口文件

package com.bjsxt.mapper;

import com.bjsxt.pojo.Flower;
import org.apache.ibatis.session.RowBounds;

import java.util.List;


public interface FlowerMapper1 {
    // 分页操作
    List<Flower> selectPage(int start, int size);

    // 逻辑分页
    List<Flower> selectPage2(RowBounds rowBounds);
}

mapper xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.FlowerMapper1">

    <!--A、分页查询-->
    <select id="selectPage" resultType="Flower">
        select * from flower limit #{param1}, #{param2}
    </select>

    <!--B、逻辑分页-->
    <select id="selectPage2" resultType="Flower">
        select * from flower
    </select>
</mapper>

Test 文件

public class TestA {

    // 分页
    @Test
    public void test2() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession();
        FlowerMapper1 flowerMapper1 = sqlSession.getMapper(FlowerMapper1.class);
        // A、物理分页
        /*List<Flower> list = flowerMapper1.selectPage(1, 2);
        System.out.println(list);*/

        // B、逻辑分页
        RowBounds rowBounds = new RowBounds(1, 2);
        List<Flower> list = flowerMapper1.selectPage2(rowBounds);
        System.out.println(list);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}
模糊查询

mapper 接口文件

public interface FlowerMapper1 {

    // 模糊查询
    List<Flower> selectMore(String keyword);
}

mapper xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.FlowerMapper1">

    <!--C、模糊查询-->
    <select id="selectMore" resultType="Flower">
        select * from flower where name like concat("%", #{param1}, "%")
    </select>
</mapper>

Test 文件

public class TestA {
    //模糊查询
    @Test
    public void test3() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession();
        FlowerMapper1 flowerMapper1 = sqlSession.getMapper(FlowerMapper1.class);
        List<Flower> list = flowerMapper1.selectMore("桂");
        System.out.println(list);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}
增删改操作

mapper 接口文件

public interface FlowerMapper2 {
    // 增加
    int insert(Flower flower);
    //修改
    int edit(Flower flower);
    //删除
    void remove(int id);
}

mapper xml 文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.FlowerMapper2">
    <!--A、新增-->
    <insert id="insert">
        insert into flower values(default, #{name}, #{price}, #{production})
    </insert>

    <!--B、修改-->
    <update id="edit">
        update flower set name = #{name}, price = #{price}, production = #{production} where id = #{id}
    </update>

    <!--C、删除-->
    <delete id="remove">
        delete from flower where id = #{param1}
    </delete>
</mapper>

Test 测试文件

public class TestA {

    // 增改删操作
    @Test
    public void test4() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession(true);
        FlowerMapper2 flowerMapper2 = sqlSession.getMapper(FlowerMapper2.class);
        // A、增加
        /*Flower flower = new Flower();
        flower.setName("绿萝");
        flower.setPrice(998);
        flower.setProduction("潮州");
        int i = flowerMapper2.insert(flower);*/

        // B、修改
        /*Flower flower = new Flower();
        flower.setId(1);
        flower.setPrice(1);
        flower.setName("新桂花");
        flower.setProduction("湖州");
        int i = flowerMapper2.edit(flower);*/

        // C、删除、
        flowerMapper2.remove(13);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}

五、SQL 查询结果填充的几种方式(面试题)

1、介绍

MyBatis 会根据映射关系把查询到的结果填充到指定结果集类型中。支持的方式:

  • auto mapping:自动映射。当列名或列的别名与实体类属性名相同时不需要做额外配置
  • resultMap:手动定义映射关系
  • camel case:驼峰命名规则

2、Auto Mapping

自动映射方式就是只要保证数据库中列名和属性名相同,就可以自动进行映射。

也可以给列名起别名,让别名和属性名对应。对应时不用区分大小写。

3、resultMap

当数据库列名和属性名不同时是无法进行自动映射的,这时候需要手动指定映射关系。

3.1 在数据库中创建表

​ 表名:无论哪种方式,表名和实体类名称是否相同都没有关系。

​ 列名:列名和实体类名称不对应。

create table tb_people2(
	peo_id int(11) primary key auto_increment,
	peo_name varchar(20)
);

insert into tb_people2 values(1,'张三');
insert into tb_people2 values(2,'李四');

3.2 创建实体类

实体类的类名和表名不是必须相同的。

实体类中属性名列名也不相同

package com.bjsxt.pojo;

public class People2 {
    private int peoId;
    private String peoName;

    public int getPeoId() {
        return peoId;
    }

    public void setPeoId(int peoId) {
        this.peoId = peoId;
    }

    public String getPeoName() {
        return peoName;
    }

    public void setPeoName(String peoName) {
        this.peoName = peoName;
    }

    @Override
    public String toString() {
        return "People2{" +
                "peoId=" + peoId +
                ", peoName='" + peoName + '\'' +
                '}';
    }
}

3.3 Mapper 接口文件

package com.bjsxt.mapper;

import com.bjsxt.pojo.People2;

import java.util.List;

public interface People2Mapper {
    List<People2> select();
}

3.4 修改映射文件

在映射文件中需要注意的是 <select> 标签不再使用 resultType 属性,而是使用 resultMap 属性。

resultMap 属性表示使用哪个 <resultMap> 作为结果映射配置。

​ 标签的子标签的property属性必须和类的属性名严格对应,区分大小写。如果没有对应上,会出现反射异常:Caused by: org.apache.ibatis.reflection.ReflectionException: There is no setter for property named ‘peoid’ in ‘class com.bjsxt.pojo.People2’。获取把数据库查询结果填充到对应属性中优先使用setter方法,如果没有setter方法,直接对属性进行赋值。

​ column属性对应数据库列名,由于数据库是不区分大小写的,所以column的值也是不区分大小写的。

​ resultMap可以存在多个。只要id属性不重名就可以。如果出现resultMap的id重名会出现报错

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjsxt.mapper.People2Mapper">
    <!-- type:数据库中每行数据对应的实体类型,支持别名 -->
    <!-- id:自定义名称-->
    <resultMap id="myid" type="People2">
        <!-- id标签定义主键列和属性的映射关系关系 -->
        <!-- property 类中属性名称,区分大小写-->
        <!-- column 表中列名-->
        <id property="peoId" column="peo_id"/>
        <!-- result标签定义非主键列和属性的映射关系-->
        <result property="peoName" column="peo_name"/>
    </resultMap>
    <select id="select" resultMap="myid">
        select * from tb_people2
    </select>
</mapper>

3.5 Test 文件测试

public class TestB {
    // 增改删操作
    @Test
    public void test1() throws IOException {
        //[1] 解析mybatis.xml
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
        //[2] 获取sqlSession 工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //[3] 获取sqlSession 对象
        SqlSession sqlSession = factory.openSession(true);
        People2Mapper mapper = sqlSession.getMapper(People2Mapper.class);
        // resultMap 映射
        List<People2> list = mapper.select();
        System.out.println(list);
        //[5]关闭sqlSession
        sqlSession.close();
    }
}

4、驼峰转换

在 MySQL 中列名命名规则是 xxx_yyy,多个单词之间使用下划线进行分割。在 Java 中属性命名规范是 xxxYyy,小驼峰的方式进行命名。这两种技术的命名习惯是不一样的,这就导致每次都需要手动配置映射关系。

MyBatis发现了这个问题,提供了驼峰转换的能力。通过全局配置文件开启驼峰转换功能后,就可以让xxx_yyy自动映射到xxxYyy上。例如:列名叫做peo_id,可以自动映射到peoId的属性上。转换时去掉列中的下划线,把下划线后面单词首字母变大写。

可以直接使用上面的示例,正好列名和属性名满足驼峰转换规则。

4.1 修改全局配置文件

在全局配置文件中添加哦配置,开启驼峰转化功能

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 读取属性文件-->
    <properties resource="jdbc.properties"></properties>

    <!--指定当前日志的类型-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <!-- 开启驼峰转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <typeAliases>
        <!-- 给包中所有的类起别名 别名就是类名 不区分大小写-->
        <package name="com.bjsxt.pojo"/>
    </typeAliases>
    <!-- 配置连接数据库环境-->
    <environments default="mysql">
        <!--mysql-->
        <environment id="mysql">
            <!--配置事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="${m_driver}"/>
                <property name="url"
                          value="${m_url}"/>
                <property name="username" value="${m_username}"/>
                <property name="password" value="${m_password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--解析FlowerMapper.xml-->
    <mappers>
        <!-- 指定目录中文件-->
        <!--<mapper resource="mapper/FlowerMapper.xml"></mapper>-->
        <!--
            如果采用的是包扫描
                A、接口必须和 xml 在同一个包中
                B、接口的名称必须和 xml 的名称保持一致
         -->
        <package name="com.bjsxt.mapper"/>
    </mappers>
</configuration>

六、接口绑定方案下参数传递

接口绑定方式其底层调用的就是前面学习的SqlSession的相关方法。

接口中方法对应以前测试类中SqlSession的方法。

对于映射文件的使用还是和之前学习的相同。

在接口绑定方案中唯一需要重点注意的是:方法带有多个参数时。当方法带有多个参数将使用session.selectList(“”,Map类型参数)或session.selectOne(“”,Map类型参数)作为底层调用。通过前面的学习清楚的知道,Map类型当做参数时,在映射文件中通过Map的key进行获取value值。

所以必须知道key是什么:

  1. 如果接口中方法没有使用注解定义名称,MyBatis使用内置名称作为key。规则:arg0、arg1、argM(M为从0开始的数字,和方法参数顺序对应)或param1、param2、paramN(N为从1开始的数字,和方法参数顺序对应)。
  2. 也可以在接口方法参数中通过@Param(“key名称”)的形式进行定义key。一但使用了注解argN的这种形式就不能使用了,但是paramN的方式还是可以使用。

1、使用注解定义参数名称

在接口中添加新方法.@Param(“key的名称”)中key的名称是自定义的,与方法参数名没有关系。

接口中方法的参数前可以加注解,也可以不加注解,也可以部分加注解部分不加注解。

public interface PeopleMapper {
    List<People> selectAll();
    // 带有多个参数
    List<People> selectByNameAddress(String name,String address);
    // 使用注解
    /*
    无论是否有注解,MyBatis中对于多参数只设置2个key。当参数前面有注解时,会使用注解名称替换到argM
    map.put("myname",name);
    map.put("myaddress",address);
    map.put("param1",name);
    map.put("param2",address);
    */
    List<People> select2(@Param("myname") String name,@Param("myaddress") String addrss);
}

在映射文件中添加新的标签,实现与接口中方法绑定。

使用注解方式时,在映射文件中argN的方式就不能使用了,但是paramN的方式还是可以使用,#{paramN}和#{key}的方式也可以混合使用

    <select id="select2" resultType="People">
        select * from people where name=#{myname} and address=#{myaddress}
    </select>
    <!--
    <select id="select2" resultType="People">
        select * from people where name=#{param1} and address=#{param2}
    </select>
    <select id="select2" resultType="People">
        select * from people where name=#{param1} and address=#{myaddress}
    </select>
    -->

2、 多个参数且包含对象类型参数

通过paramN或argM的方式调用,如果这个参数是对象通过paramN.属性名或argN-1.属性名可以调用对象中的属性。

与上面的道理相同,接口方法参数也可以添加注解。使用注解了argM的形式就不能使用了,需要通过注解中名称或paramN的方式调用。

在接口中添加新的方法,设定映射文件需要获取peo的id属性值,name参数的值,peo2的address值

// 需要使用peo中id,name,peo2的address
List<People> select3(People peo,String name,@Param("peo2") People peo2);

在映射文件中添加标签,与接口中的方法进行绑定.

​ People peo:可以通过arg0、param1两种方式获取到。arg0.属性名、param1.属性名获取对象的属性。

​ String name:可以通过arg1、param2两种方式获取到。

​ People peo2:可以通过peo2、param3两种方式获取到。peo2.属性名、param3.属性名获取对象属性。

 <select id="select3" resultType="People">
        select * from people where id=#{arg0.id} and name=#{arg1} and address=#{peo2.address}
 </select>

七、主键回填

在一些特殊的需求中,需要执行完新增的SQL后立即知道新增时主键的值。如果主键的值是自己设置的固定值,可以知道主键是多少。但是很多时候都是使用MySQL的主键自增,这时想要获取主键的值就需要通过特殊的方式获取了。在MyBatis中有两种方式可以获取到自增主键的值:

  • 使用<selectKey>子标签编写SQL进行回填属性。
  • 使用<select>的useGeneratedKeys属性进行自动回填

1、 selectKey的使用

1.1 在接口中填写方法

在接口中添加方法。

 // 参数peo中包含了name值和address值。
 int insert1(People peo);

1.2 在映射文件中添加标签

在映射文件中添加标签与接口中方法进行绑定。

<selectKey>标签是<insert>的子标签,作用:把查询到的结果填充进行回填

​ keyProperty:接口方法参数中,对象的哪个属性需要进行回填

​ resultType:SQL查询到的结果

​ select @@identity:是MySQL内置的全局变量表示获取到自增主键值。

​ order:selectKey中SQL是在外面SQL执行之前还是之后。可取值:AFTER和BEFORE

    <insert id="insert1">
        <selectKey keyProperty="id" resultType="int">
            select @@identity
        </selectKey>
        insert into people values(default,#{name},#{address})
    </insert>

编写测试类

public class Test {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.cfg.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();
        PeopleMapper peopleMapper = session.getMapper(PeopleMapper.class);
        People peo3 = new People();
        peo3.setName("李四");
        peo3.setAddress("北京经济开发区");
        int index = peopleMapper.insert1(peo3);
        System.out.println(peo3);// peo3中带有主键值
        session.close();
    }
}

2、自动主键回填

MyBatis的映射文件的<insert>标签带有自动主键回填功能,只需要设置useGeneratedKeys进行开启自动主键回填功能,同时设置keyProperty的值需要回填到对象的哪个属性。

直接修改映射文件中insert1的内容,修改后运行测试类,查看控制台输出对象值是否带有主键值。

    <insert id="insert1" useGeneratedKeys="true" keyProperty="id">
        insert into people values(default,#{name},#{address})
    </insert>

八、动态 SQL

1、动态 SQL 引入

经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?

如果采用JDBC进行处理,需要根据条件是否取值进行SQL语句的拼接,一般情况下是使用StringBuilder类及其append方法实现,还是有些繁琐的。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护

MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。

为了查看动态SQL最终执行时的SQL,先把日志配置上

2、if 标签

通过if处理用户多变的查询条件

接口代码

List<People> selectIf(People people);

mapper映射文件通过if进行判断参数的属性是否为null。

<if>标签的test属性值为OGNL(对象导航图语言)表达式,通过对象属性名可以快速获取到对象属性值。

代码解释说明:

​ name!=null : OGNL 表达式,直接写属性名可以获取到属性值。不需要添加${}或#{}

​ name=#{name} 中name是SQL的列名。#{name}是MyBatis获取参数对象属性值的写法(之前学习的)。

​ where 1=1 中1=1是为了保证SQL语法的正确性。如果if成立没有1=1最后的SQL就是where and name=xxx 这种写法是不对的。

    <select id="selectIf" resultType="People">
        select * from people where 1=1
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="address!=null">
            and address=#{address}
        </if>
    </select>

通过测试类中是否设置对象属性值来控制SQL,如果name属性有值,address属性没有值。最终执行的SQL就没有and name=xxx

public class Test {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.cfg.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();
        PeopleMapper peopleMapper = session.getMapper(PeopleMapper.class);
        People peo = new People();
        peo.setName("张三");
        //peo.setAddress("北京海淀");
        List<People> list = peopleMapper.selectIf(peo);
        System.out.println(list);
        session.close();
    }
}

3、choose 标签

choose标签相当于Java中的switch…case…default。在choose标签里面可以有多个when标签和一个otherwise(可以省略)标签。只要里面有一个when成立了后面的when和otherwise就不执行了。

   <select id="selectIf" resultType="People">
        select * from people where 1=1
        <choose>
            <when test="name!=null">
                and name=#{name}
            </when>
            <when test="address!=null">
                and address=#{address}
            </when>
            <otherwise>
                // do something
            </otherwise>
        </choose>
    </select>

4、trim 标签

上面的if标签中为了保证语法的正确性,需要在SQL中明确指定where 1=1,其中1=1存在的意义单纯为了保证语法的正确性,没有实际意义的。可以通过trim标签动态进行截取添加,省略where 1=1。

trim标签包含四个属性:

​ prefix:只要内容不是空字符串(“”),就在子内容前面添加特定字符串。

​ prefixOverrides:如果里面内容是以某个内容开头,去掉这个内容。

​ suffix:只要内容不是空字符串(“”),就在子内容后面添加特定字符串。

​ suffixOverrides:如果里面内容以某个内容结尾,就去掉这个内容。

小提示:

​ trim作为很多其他标签的底层。

​ 无论是开头操作还是结尾的操作,都是先去掉容,后添加。

​ trim只会对里面的子内容进行操作。如果子内容为空则不进行任何操作。

​ 后添加的内容会有空格。

特例:

​ 如果内部字符串为要去掉的字符串,去掉后认为内容不为空,prefix依然添加。

	<select id="selectIf" resultType="People">
        select * from people
        <trim prefix="where" prefixOverrides="and">
            <if test="name!=null">
                and name=#{name}
            </if>
            <if test="address!=null">
                and address=#{address}
            </if>
        </trim>
    </select>

5、where 标签

where标签属于trim标签的简化版,被where标签包含的内容具备:

​ 如果里面内容不为空串,在里面内容最前面添加where

​ 如果里面内容是以and开头,去掉最前面的and

    <select id="selectIf" resultType="People">
        select * from people
        <where>
            <if test="name!=null">
                and name=#{name}
            </if>
            <if test="address!=null">
                and address=#{address}
            </if>
        </where>
    </select>

6、set 标签

set标签是专门用在修改SQL中的,属于trim的简化版,带有下面功能:

  • 如果子内容不为空串,在最前面添加set
  • 去掉最后一个逗号

6.1 代码演示流程

在接口中添加修改方法

int update(People people);

在映射文件中编写动态SQL。

set标签里面id=#{id}是非常重要的,不能不写。因为set在解析时,如果里面为空串,是不会在前面添加set的,对于SQL的修改来说,没有set关键字是不正确的语法。

    <update id="update">
        update people
        <set>
            <if test="name!=null">
                name=#{name},
            </if>
            <if test="address!=null">
                address=#{address},
            </if>
            id=#{id}
        </set>
        where id = #{id}
    </update>

在测试类中测试。

public class Test {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.cfg.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();
        PeopleMapper peopleMapper = session.getMapper(PeopleMapper.class);
        People peo = new People();
        peo.setName("张三2");
        peo.setAddress("北京海淀2");
        peo.setId(1);
        int index = peopleMapper.update(peo);
        System.out.println(index);
        session.commit();
        session.close();
    }
}

7、foreach 标签

foreach标签表示循环,主要用在in查询或批量新增的情况。

foreache标签的属性解释说明

​ collection:要遍历的数组或集合对象。如果参数没有使用@Param注解:arg0或array(数组)|list(集合).如果使用@Param注解,使用注解的名称或param1

​ open:遍历结束在前面添加的字符串

​ close:遍历结束在后面添加的字符串

​ item:迭代变量。在foreach标签里面#{迭代变量}获取到循环过程中迭代变量的值。

​ separator:分隔符。在每次循环中间添加的分割字符串。

​ index:迭代的索引。从0开始的数字。

7.1 代码演示

在接口中添加方法

List<People> selectByIds(int[] ids);

在映射文件中添加下面代码。

由于collection属性值的重要提醒:

​ 如果方法参数前面没有添加@Param注解,只能通过array或arg0获取到数组。

​ 如果方法参数前面添加了@Param注解,只能通过注解值或param1获取到数组。

<select id="selectByIds" resultType="People">
    select * from people where id in
    <foreach collection="array" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>

8、bind 标签

bind标签表示对传递进来的参数重新赋值。最多的使用场景为模糊查询。通过bind可以不用在Java代码中对属性添加%

List<People> selectLike(People peo);

映射文件中需要注意value属性值中进行字符串拼接。%两次要有单引号,name没有单引号。

    <select id="selectLike" resultType="People">
        <bind name="name" value="'%'+name+'%'"/>
        select * from people where name like #{name}
    </select>

9、sql 和 include 标签

在企业开发中的表可能都会有很多列,当使用多表联合查询时列的个数更多。很多功能或SQL都需要使用这些列的话,其实在做很多重复工作。及时复制粘贴,一旦碰到表结构改变或添加、删除列的时候也需要修改很多SQL。

MyBatis的sql标签用于定义SQL片段,include标签用于引用sql标签定义的片段。

List<People> selectSQL();

使用sql标签定义sql片段,使用include引用sql片段

    <select id="selectSQL" resultType="People">
        select <include refid="mysqlpart"></include> from people
    </select>
    <sql id="mysqlpart">
        id,name,address
    </sql>

九、MyBatis 中常用注解

在MyBatis中对于特别简单的SQL、尤其不需要定义resultMap的SQL可以使用注解进行实现。通过注解能简化映射文件的编写。

MyBatis的注解通过全局配置文件<mappers>进行加载注解

  • 如果一个Mapper接口中所有方法都使用注解定义SQL,可以在全局文件中配置
    <mappers>
        <mapper class="com.bjsxt.mapper.PeopleMapper"></mapper>
    </mappers>
  • 如果一个Mapper接口中既有注解又有mapper.xml定义SQL。可以在全局配置文件中通过<package>进行加载。这种方式和之前的接口绑定方案的配置是一样的。也就是说MyBatis在扫描这个包的时候就可以加载到注解。
    <mappers>
        <package name="com.bjsxt.mapper"/>
    </mappers>

在MyBatis中注解都是写在Mapper接口的方法上中,所有的注解都在org.apache.ibatis.annotations包中,常见注解:

注解解释
@Select查询
@Insert新增
@Delete删除
@Update修改
@SelectKey主键回填
@SelectProvider调用SQL构建器。查询专用
@InsertProvider调用SQL构建器。添加专用
@UpdateProvider调用SQL构建器。修改专用
@DeleteProvider调用SQL构建器。删除专用
@Param定义参数的名称

1、CURD 操作实例

简单的CURD操作直接在方法上添加对应类型的注解即可。

public interface PeopleMapper {
    @Select("select * from people")
    List<People> selectAll();
    @Insert("insert into people values(default,#{name},#{address})")
    int insert(People peo);
    @Delete("delete from people where id=#{id}")
    int deleteById(int id);
    @Update("update people set name=#{name},address=#{address} where id=#{id}")
    int updateById(People peo);
}

2、主键回填

使用注解时,主键回填需要通过@SelectKey注解。该注解中:

​ keyProperty:必有属性。表示回填属性名

​ statement:执行的sql

​ before:必有属性。表示是否在@Insert的SQL之前执行。

​ resultType:必有属性。表示statement对应SQL执行结果

    @Insert("insert into people values(default,#{name},#{address})")
    @SelectKey(keyProperty = "id",statement = "select @@identity",before = false,resultType = Integer.class)
    int insert2(People people);
@Insert("insert into people values(default,#{name},#{address})")
@Options(useGeneratedKeys = true,keyProperty = "id",keyColumn = "id")
int insert2(People people);

3、SQL 构建器(Provider)

在以后工作时,有的功能对应SQL可能会非常长,在Navicat中一页显示不下。对于这种情况可以使用映射文件没有什么太大问题。如果放在@Select注解的参数中,显然看起来不是太方便。如果还是希望使用注解,最好使用SQL构建器。

MyBatis的SQL构建器赋予了程序员在Java类中编写SQL的方式。把以前写在注解参数中的复杂SQL转移到了类的方法中进行书写。

3.1直接写SQL方式代码演示

在接口中提供方法。并在方法上面添加@SelectProvider注解,注解中属性含义:

  • type:编写SQL的类
  • method:类中哪个方法返回SQL
@SelectProvider(type = MySQLProvider.class,method = "selectprovider")
List<People> select(People peo);

新建MySQLProvider类,并在类中提供selectprovider方法。

下面代码并不是必须写成多行,只是为了演示当SQL比较复杂时都是分多行写的效果。

返回SQL的方法必须是public String的。

小提示:

​ 下面SQL看起来写的不是特别费劲,但是一定要注意关键字前后都有空格。下面代码每行前面都有空格,其实这点非常不友好。

public class MySQLProvider {
    public String selectprovider(){
        return "select *" +
                " from people" +
                " where name=#{name} and address like #{address}" +
                " order by id desc";
    }
}

3.2 使用SQL类

MyBatis提供了SQL类,该类中封装了很多方法,方法名称和SQL的关键字名称正好对应。

里面需要注意的点:

1、没有and关键字的方法。如果多个条件可以放在一个where中,也可以放在多个连续where中。

2、最终需要调用toString()转换为字符串。

package com.bjsxt.provider;

import org.apache.ibatis.jdbc.SQL;

public class MySQLProvider {
    public String selectprovider2(){
        return "select *" +
                "from people" +
                "where name=#{name} and address like #{address}" +
                "order by id desc";
    }
    public String selectprovider(){
        return new SQL()
                .SELECT("*")
                .FROM("people")
                .WHERE("name=#{name}")
                // 没有and,多个并列条件使用WHERE方法
                .WHERE("address like #{address}")
                .ORDER_BY("id desc")
                .toString();
    }
}

4.使用注解进行结果映射

@Results的value属性类型Result[]

@Result注解:

​ column:数据库列

​ property:属性名

​ id:是否为主键,默认false

    @Results(value = {
            @Result(column = "peo_id",property = "id",id = true),
            @Result(column = "peo_name",property = "name")
    })
    @Select("select * from tb_people where peo_name=#{name}")
    List<People> select2(People peo);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值