目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Mybatis Reference

官网

log4j

src/main/resources/log4j.properties

 1### direct log messages to stdout ###
 2log4j.appender.stdout=org.apache.log4j.ConsoleAppender
 3log4j.appender.stdout.Target=System.out
 4log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 5log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 6
 7### direct messages to file mylog.log ###
 8log4j.appender.file=org.apache.log4j.FileAppender
 9log4j.appender.file.File=c:/mylog.log
10log4j.appender.file.layout=org.apache.log4j.PatternLayout
11log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
12
13### set log levels - for more verbose logging change 'info' to 'debug' ###
14
15log4j.rootLogger=debug, stdout

依赖

1<!-- 分页助手 -->
2        <dependency>
3            <groupId>log4j</groupId>
4            <artifactId>log4j</artifactId>
5            <version>1.2.17</version>
6        </dependency>

MyBatis 执行流程

基本执行流程

快速入门

  1. 创建 mybatis_db 数据库和 user 表
  2. 创建项目,导入依赖
  3. 创建 User 实体类
  4. 编写映射文件 UserMapper.xml
  5. 编写核心文件 SqlMapConfig.xml
  6. 编写测试类

创建数据及 user 表

1CREATE DATABASE `mybatis_db`; USE `mybatis_db`; 
2
3CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` datetime default NULL COMMENT '生日', `sex` char(1) default NULL COMMENT '性别', `address` varchar(256) default NULL COMMENT '地址',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4
5insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'子 慕','2020-11-11 00:00:00','男','北京海淀'),(2,'应颠','2020-12-12 00:00:00','男','北 京海淀');

创建 Maven 工程,导入依赖(Mysql 驱动、MyBatis、junit)

 1<!--指定编码及版本-->
 2<properties>
 3    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 4    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
 5    <java.version>11</java.version>
 6    <maven.compiler.source>11</maven.compiler.source>
 7    <maven.compiler.target>11</maven.compiler.target>
 8</properties>
 9
10<dependencies>
11    <!-- 引入mybatis依赖-->
12    <dependency>
13        <groupId>org.mybatis</groupId>
14        <artifactId>mybatis</artifactId>
15        <version>3.5.4</version>
16    </dependency>
17
18    <!-- 引入mysql依赖-->
19    <dependency>
20        <groupId>mysql</groupId>
21        <artifactId>mysql-connector-java</artifactId>
22        <version>5.1.47</version>
23    </dependency>
24
25    <!-- 引入JUnit依赖-->
26    <dependency>
27        <groupId>junit</groupId>
28        <artifactId>junit</artifactId>
29        <version>4.12</version>
30    </dependency>
31
32</dependencies>

编写 User 实体类

 1package com.soulboy;
 2
 3import java.util.Date;
 4
 5public class User {
 6    private Integer id;
 7    private String username;
 8    private Date birthday;
 9    private String sex;
10    private String address;
11
12    public Integer getId() {
13        return id;
14    }
15
16    public void setId(Integer id) {
17        this.id = id;
18    }
19
20    public String getUsername() {
21        return username;
22    }
23
24    public void setUsername(String username) {
25        this.username = username;
26    }
27
28    public Date getBirthday() {
29        return birthday;
30    }
31
32    public void setBirthday(Date birthday) {
33        this.birthday = birthday;
34    }
35
36    public String getSex() {
37        return sex;
38    }
39
40    public void setSex(String sex) {
41        this.sex = sex;
42    }
43
44    public String getAddress() {
45        return address;
46    }
47
48    public void setAddress(String address) {
49        this.address = address;
50    }
51  
52    @Override
53    public String toString() {
54        return "User{" +
55                "id=" + id +
56                ", username='" + username + '\'' +
57                ", birthday=" + birthday +
58                ", sex='" + sex + '\'' +
59                ", address='" + address + '\'' +
60                '}';
61    }
62}

编写 UserMapper.xml 映射配置文件(ORM 思想)

src/main/resources/mapper/UserMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<!--
 7namespace命名空间: 与ID属性共同构成唯一标识 user.findAll
 8resultType结果类型: 返回结果类型(自动映射封装):要封装的实体的全路径
 9-->
10<mapper namespace="userMapper">
11    <!-- 查询所有 -->
12    <select id="findAll" resultType="com.soulboy.domain.User">
13        select * from user
14    </select>
15
16    <!-- 新增用户  #{}是Mybatis中的占位符,等同于JDBC中的? -->
17    <insert id="saveUser" parameterType="com.soulboy.domain.User">
18        insert into user(username,birthday,sex,address) values (#{username},#{birthday},#{sex},#{address})
19    </insert>
20
21    <!-- 更新用户 -->
22    <update id="updateUser" parameterType="com.soulboy.domain.User">
23        update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id = #{id}
24    </update>
25
26    <!-- 删除用户 -->
27    <delete id="deleteUser" parameterType="java.lang.Integer">
28        delete from user where id = #{abc}
29    </delete>
30</mapper>

编写 MyBatis 核心配置文件

1、数据库环境配置 src/main/resources/sqlMapConfig.xml

2、映射关系配置的引入(引入映射配置文件的路径)

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE configuration
 3        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5
 6<configuration>
 7    <!-- 运行环境 -->
 8    <environments default="development">
 9        <environment id="development">
10            <!-- 当前的事务交由JDBC进行管理 -->
11            <transactionManager type="JDBC"></transactionManager>
12            <!-- 数据源信息 POOLED:使用Mybatis的连接池-->
13            <dataSource type="POOLED">
14                <property name="driver" value="com.mysql.jdbc.Driver"/>
15                <property name="url" value="jdbc:mysql://localhost:50000/MyBatis"/>
16                <property name="username" value="root"/>
17                <property name="password" value="123456"/>
18            </dataSource>
19        </environment>
20    </environments>
21
22    <!-- 引入映射配置文件 -->
23    <mappers>
24        <mapper resource="mapper/UserMapper.xml"></mapper>
25    </mappers>
26</configuration>

编写测试类

src/main/java/com/soulboy/test/MybatisTest.java

  1package com.soulboy.test;
  2
  3import com.soulboy.domain.User;
  4import org.apache.ibatis.io.Resources;
  5import org.apache.ibatis.session.SqlSession;
  6import org.apache.ibatis.session.SqlSessionFactory;
  7import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  8import org.junit.Test;
  9
 10import java.io.IOException;
 11import java.io.InputStream;
 12import java.util.Date;
 13import java.util.List;
 14
 15public class MybatisTest {
 16    /*
 17        快速入门测试方法
 18    */
 19    @Test
 20    public void mybatisQuickStart() throws IOException {
 21        //1.加载核心配置文件
 22        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 23        //2.获取sqlSessionFactory工厂对象
 24        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 25        //3.获取sqlSession会话对象
 26        SqlSession sqlSession = sqlSessionFactory.openSession();
 27        //4.执行sql 参数:statementid:namespace.id
 28        List<User> users = sqlSession.selectList("userMapper.findAll");
 29        //5.遍历打印查询结果集
 30        for (User user : users) {
 31            System.out.println(user);
 32        }
 33        //6.关闭资源
 34        sqlSession.close();
 35    }
 36
 37    /*
 38        测试新增用户
 39    */
 40    @Test
 41    public void testSave() throws IOException {
 42        //1.加载核心配置文件
 43        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 44        //2.获取sqlSessionFactory工厂对象
 45        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 46        //3.获取sqlSession会话对象
 47        SqlSession sqlSession = sqlSessionFactory.openSession();
 48        //4.执行sql 参数:statementid:namespace.id
 49        User user = new User();
 50        user.setUsername("高中美");
 51        user.setBirthday(new Date());
 52        user.setSex("女");
 53        user.setAddress("河南开封");
 54        sqlSession.insert("userMapper.saveUser", user);
 55        //5.关闭资源
 56        sqlSession.commit();
 57        sqlSession.close();
 58    }
 59
 60    /*
 61        测试更新用户
 62    */
 63    @Test
 64    public void testUpdate() throws IOException {
 65        //1.加载核心配置文件
 66        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 67        //2.获取sqlSessionFactory工厂对象
 68        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 69        //3.获取sqlSession会话对象
 70        SqlSession sqlSession = sqlSessionFactory.openSession();
 71        //4.执行sql 参数:statementid:namespace.id
 72        User user = new User();
 73        user.setId(3);
 74        user.setUsername("高中直");
 75        user.setBirthday(new Date());
 76        user.setSex("男");
 77        user.setAddress("河南开封");
 78        sqlSession.update("userMapper.updateUser", user);
 79        //5.关闭资源
 80        sqlSession.commit();
 81        sqlSession.close();
 82    }
 83
 84    /*
 85           测试更新用户
 86       */
 87    @Test
 88    public void testDelete() throws IOException {
 89        //1.加载核心配置文件
 90        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 91        //2.获取sqlSessionFactory工厂对象
 92        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 93        //3.获取sqlSession会话对象
 94        SqlSession sqlSession = sqlSessionFactory.openSession();
 95        //4.执行sql 参数:statementid:namespace.id
 96        sqlSession.delete("userMapper.deleteUser", 3);
 97        //5.关闭资源
 98        sqlSession.commit();
 99        sqlSession.close();
100    }
101}

DAO 层传统开发方式:接口

需要手动编写接口、实现 类、XXXMapper.xml 文件

缺点
Impl 实现类中模板代码冗余
Impl 实现类中硬编码:List users = sqlSession.selectList("userMapper.findAll")
  1. 接口
    com/soulboy/dao/IUserDao.java
 1package com.soulboy.dao;
 2
 3import com.soulboy.domain.User;
 4import java.io.IOException;
 5import java.util.List;
 6
 7public interface IUserDao {
 8/**
 9
10* 查询所有
11  */
12  public List<User> findAll() throws IOException;
13  }
  1. 实现类
    com/soulboy/dao/impl/UserDaoImpl.java
 1public class UserDaoImpl implements IUserDao {
 2
 3	@Override
 4	public List<User> findAll() throws IOException {
 5		InputStream resourceAsStream = 				Resources.getResourceAsStream("sqlMapConfig.xml");
 6		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 7		SqlSession sqlSession = sqlSessionFactory.openSession();
 8		List`<User>` users = sqlSession.selectList("userMapper.findAll");
 9		return users;
10	}
11
12}
  1. 映射文件
    mapper/UserMapper.xml
 1<?xml version="1.0" encoding="UTF-8" ?>
 2
 3<!DOCTYPE mapper
 4        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 5        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 6
 7<!--
 8namespace命名空间: 与ID属性共同构成唯一标识 user.findAll
 9resultType结果类型: 返回结果类型(自动映射封装):要封装的实体的全路径
10typeAliases别名: com.soulboy.domain.User
11-->
12
13<mapper namespace="userMapper">
14    <!-- 查询所有 -->
15    <select id="findAll" resultType="user">
16        select * from user
17    </select>
18</mapper>
  1. 测试代码
 1public class MybatisTest {
 2    /**
 3     * mybatis的dao层传统开发方式测试
 4    */
 5    @Test
 6    public void Test1() throws IOException {
 7        //调用持久层方法
 8        IUserDao userDao = new UserDaoImpl();
 9        List<User> users = userDao.findAll();
10        for (User user : users) {
11            System.out.println(user);
12        }
13    }
14}

DAO 层代理开发方式:接口代理

基于接口代理实现持久层是企业的主流开发方式

开发规范
基于接口代理

开发规范
接口代理底层原理

  1. 接口
    com/soulboy/mapper/UserMapper.java
1public interface UserMapper {
2    /**
3     * 根据id查询用户
4     */
5    public User findUserById(int id);
6
7}
  1. 映射文件
    com/soulboy/mapper/UserMapper.xml
 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5<!--  -->
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7    <select id="findUserById" parameterType="int" resultType="user">
 8        select * from user where id = #{id}
 9    </select>
10</mapper>
  1. sqlMapConifg.xml
 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE configuration
 3        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5
 6<configuration>
 7    <!-- 加载properties文件 -->
 8    <properties resource="jdbc.properties"></properties>
 9    <!-- 设置别名 -->
10    <typeAliases>
11        <!-- 方式一: 给单个实体起别名 -->
12        <!-- <typeAlias type="com.soulboy.domain.User" alias="user"></typeAlias> -->
13        <!-- 方式二: 批量起别名 -->
14        <package name="com.soulboy.domain"/>
15    </typeAliases>
16    <!-- 运行环境 -->
17    <environments default="development">
18        <environment id="development">
19            <!-- 当前的事务交由JDBC进行管理 -->
20            <transactionManager type="JDBC"></transactionManager>
21            <!-- 数据源信息 POOLED:使用Mybatis的连接池-->
22            <dataSource type="POOLED">
23                <property name="driver" value="${jdbc.driver}"/>
24                <property name="url" value="${jdbc.url}"/>
25                <property name="username" value="${jdbc.username}"/>
26                <property name="password" value="${jdbc.password}"/>
27            </dataSource>
28        </environment>
29    </environments>
30    <!-- 引入映射配置文件 -->
31    <mappers>
32        <!-- 使用该方式: XML文件的全路径-->
33        <!--<mapper resource="com.soulboy/mapper/UserMapper.xml"></mapper>-->
34
35        <!-- 使用该方式: 接口和映射文件需要同包同名 -->
36        <!--<mapper class="com.soulboy.mapper.UserMapper"></mapper>-->
37
38        <!-- 批量加载映射: Mapper接口的包路径 -->
39         <package name="com.soulboy.mapper"/>
40    </mappers>
41</configuration>

src/main/resources/jdbc.properties 文件

1jdbc.driver=com.mysql.jdbc.Driver
2jdbc.url=jdbc:mysql://localhost:50000/MyBatis?useSSL=false
3jdbc.username=root
4jdbc.password=123456
  1. 测试类
 1public class MybatisTest {
 2    /**
 3     * mybatis的dao层传统开发方式测试
 4    */
 5    @Test
 6    public void Test1() throws IOException {
 7        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 8        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 9        SqlSession sqlSession = sqlSessionFactory.openSession();
10        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
11        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
12        User user = userMapper.findUserById(1);
13        System.out.println(user);
14    }
15}

高级查询

ResultType、ResultMap

属性名 功能
ResultType 如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中
ResultMap 如果实体的属性名与表中字段名不一致,可以使用 ResutlMap 实现手动封装到实体类中

当实体属性名与数据库表中字段名不一致的时候,可以采用 ResultMap 解决。

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7    <!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装
 8        id: 标签的唯一标识
 9        type: 封装后实体类型
10        property:实体的属性名
11        column: 表中字段的属性名
12        <id>: 主键的封装
13        <result>: 表中普通字段的封装
14     -->
15    <resultMap id="userResultMap" type="com.soulboy.domain.User">
16        <id property="id" column="id"></id>
17        <result property="usernameabc" column="username"></result>
18        <result property="birthdayabc" column="birthday"></result>
19        <result property="sexabc" column="sex"></result>
20        <result property="addressabc" column="address"></result>
21    </resultMap>
22
23    <!-- 查询指定id的用户 -->
24    <select id="findUserById" parameterType="int" resultMap="userResultMap">
25        select * from user where id = #{id}
26    </select>
27
28    <!-- 查询所有用户 -->
29    <select id="findAllResultMap" resultMap="userResultMap">
30        select * from user
31    </select>
32</mapper>

多条件查询

1根据id和username查询

方式一:使用 #{arg0}~#{argn} 或者 #{param1}~#{paramn} 获取参数

UserMapper 接口

1/**
2     * 根据id和username查询user表
3     */
4    public List<User>  findByIdAndUserName1(int id, String username);

UserMapper.xml

1<!-- 多条件查询: 方式一 -->
2    <select id="findByIdAndUserName1" resultMap="userResultMap" >
3        select * from user where id = #{arg0} and username = #{arg1}
4    </select>

测试类

 1/**
 2     *
 3     * 多条件查询: 方式一
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test3() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        List<User> users = userMapper.findByIdAndUserName1(5, "高中美");
14        for (User user : users) {
15            System.out.println(user);
16        }
17        sqlSession.close();
18    }

方式二:注解,引入 @Param() 注解获取参数
UserMapper 接口

1/**
2     * 根据id和username查询user表:方式二
3     */
4    public List<User>  findByIdAndUserName2(@Param("id") int id, @Param("username") String username);

UserMapper.xml

1<!-- 多条件查询: 方式二 -->
2    <select id="findByIdAndUserName2" resultMap="userResultMap" >
3        select * from user where id = #{id} and username = #{username}
4    </select>

测试类

 1/**
 2     *
 3     * 多条件查询: 方式二
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test4() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        List<User> users = userMapper.findByIdAndUserName1(5, "高中美");
14        for (User user : users) {
15            System.out.println(user);
16        }
17        sqlSession.close();
18    }

方式三:pojo 对象传递参数
UserMapper 接口

1/**
2     * 根据id和username查询user表:方式三
3     */
4    public List<User>  findByIdAndUserName3(User user);

UserMapper.xml

1<!-- 多条件查询: 方式三 -->
2    <select id="findByIdAndUserName3" resultMap="userResultMap" parameterType="user">
3        select * from user where id = #{id} and username = #{usernameabc}
4    </select>

测试类

 1/**
 2     *
 3     * 多条件查询: 方式三
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test5() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        User user1 = new User();
14        user1.setId(5);
15        user1.setUsernameabc("高中美");
16
17        List<User> users = userMapper.findByIdAndUserName3(user1);
18
19        for (User user : users) {
20            System.out.println(user);
21        }
22        sqlSession.close();
23    }

模糊查询

名称 功能
${} 1. 通过 #{} 可以实现 preparedStatement 向占位符中设置值,自动进行 Java 类型和 JDBC 类型转换,#{}可以有效防止 SQL 注入。2. #{} 可以接收简单类型值或 pojo 属性值。 3. 如果 parameterType 传输单个简单类型值, #{} 括号中名称随便写。
#{} 1. 通过${}可以将 parameterType 传入的内容拼接在 SQL 中且不进行 JDBC 类型转换,会出现 SQL 注入问题。2.${} 可以接收简单类型值或 pojo 属性值。3.如果 parameterType 传输单个简单类型值, ${} 括号中只能是 value。

根据 username 模糊查询 user 表

方式一
UserMapper 接口

1/**
2     * 模糊查询:方式一
3     * @param username
4     * @return
5     */
6    public List<User> findByUsername1(String username);

UserMapper.xml

1<!-- 模糊查询: 方式一  其实可以随便写-->
2    <select id="findByUsername1" resultMap="userResultMap" parameterType="string">
3        <!-- #{}在mybatis中是占位符,引用参数值的时候会自动添加单引号 -->
4        select * from user where username like #{usernameabc}
5    </select>

测试类

 1/**
 2     *
 3     * 模糊查询: 方式一
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test6() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        List<User> users = userMapper.findByUsername1("%中%");
14        for (User user : users) {
15            System.out.println(user);
16        }
17        sqlSession.close();
18    }

方式二
UserMapper 接口

1/**
2     * 模糊查询:方式二
3     * @param username
4     * @return
5     */
6    public List<User> findByUsername2(String username);

UserMapper.xml

1<!-- 模糊查询: 方式二  -->
2    <select id="findByUsername2" resultMap="userResultMap" parameterType="string">
3        <!-- parameterType是基本数据类型或者String的时候,${}里面的值只能写value
4            ${}:sql原样拼接
5         -->
6        select * from user where username like '${value}'
7    </select>

测试类

 1/**
 2     *
 3     * 模糊查询: 方式二
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test7() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        List<User> users = userMapper.findByUsername2("%中%");
14        for (User user : users) {
15            System.out.println(user);
16        }
17        sqlSession.close();
18    }

映射文件深入

返回主键

我们很多时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。

方式一:useGeneratedKeys
注意:只适用于主键自增的数据库,MySQL 和 sqlserver 支持,Oracle 不行。

UserMapper 接口

1/**
2     * 添加用户并获取返回主键:方式一
3     *
4     * @param
5     * @return
6     */
7    public void saveUser(User user);

UserMapper 映射文件

1<insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
2        insert into user(username,birthday,sex,address) values(#{usernameabc},#{birthdayabc},#{sexabc},#{addressabc})
3    </insert>

测试类

 1/**
 2     *
 3     * 添加用户并返回主键: 方式一
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test8() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        User user = new User();
14        user.setUsernameabc("高中雅");
15        user.setBirthdayabc(new Date());
16        user.setAddressabc("河南开封");
17        user.setSexabc("女");
18        System.out.println(user); //User{id=null, usernameabc='高中雅', birthdayabc=Fri Oct 28 14:42:08 CST 2022, sexabc='女', addressabc='河南开封'}
19        userMapper.saveUser(user);
20        System.out.println(user); //User{id=6, usernameabc='高中雅', birthdayabc=Fri Oct 28 14:42:08 CST 2022, sexabc='女', addressabc='河南开封'}
21	sqlSession.commit();
22        sqlSession.close();
23    }

方式二:selectKey

UserMapper 接口

1/**
2     * 添加用户并获取返回主键:方式二
3     *
4     * @param
5     * @return
6     */
7    public void saveUser2(User user);

UserMapper 映射文件

 1<!-- 添加用户并返回主键: 方式二
 2        selectKey支持所有类型的数据库,适用范围更广.
 3        order: 支持主键自增的用AFTER(mysql、sqlserver),不支持主键自增的用BEFORE(oracle)
 4        keyColumn: 指定表中主键字段名
 5        keyProperty: 把返回主键的值,封装到实体中的指定属性
 6        resultType: 指定主键类型
 7    -->
 8    <insert id="saveUser2" parameterType="user" >
 9        <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
10            select LAST_INSERT_ID();
11        </selectKey>
12        insert into user(username,birthday,sex,address) values(#{usernameabc},#{birthdayabc},#{sexabc},#{addressabc})
13    </insert>

测试类

 1/**
 2     *
 3     * 添加用户并返回主键: 方式二
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test9() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        User user = new User();
14        user.setUsernameabc("妞妞");
15        user.setBirthdayabc(new Date());
16        user.setAddressabc("河南开封");
17        user.setSexabc("女");
18        System.out.println(user); //User{id=null, usernameabc='妞妞', birthdayabc=Fri Oct 28 15:06:13 CST 2022, sexabc='女', addressabc='河南开封'}
19        userMapper.saveUser2(user);
20        System.out.println(user); //User{id=10, usernameabc='妞妞', birthdayabc=Fri Oct 28 15:06:13 CST 2022, sexabc='女', addressabc='河南开封'}
21        sqlSession.commit();
22        sqlSession.close();
23    }

动态 SQL 之 if

根据 id 和 username 查询,但是不确定两个都有值

UserMapper 接口

1/**
2     * 动态sql的if标签: 多条件查询
3     * @param
4     * @return
5     */
6    public List<User> findByUsernameIf(User user);

UserMapper 映射文件

 1<!-- 动态sql之if: 多条件查询 -->
 2    <select id="findByUsernameIf" parameterType="user" resultMap="userResultMap">
 3        select * from user
 4        <where>
 5            <!-- test里面写的就是表达式 -->
 6            <if test="id != null">
 7                and id = #{id}
 8            </if>
 9            <if test="usernameabc != null">
10                and username like #{usernameabc}
11            </if>
12        </where>
13    </select>

测试类

 1/**
 2     *
 3     * 动态sql之if: 多条件查询 
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test10() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13
14        User user = new User();
15        //user.setId(8);
16        user.setUsernameabc("%妞%");
17
18        List<User> users = userMapper.findByUsernameIf(user);
19        for (User user1 : users) {
20            System.out.println(user1);  //User{id=10, usernameabc='妞妞', birthdayabc=Fri Oct 28 15:09:52 CST 2022, sexabc='女', addressabc='河南开封'}
21
22        }
23        sqlSession.close();
24    }

动态 SQL 之 set

动态更新 user 表数据,如果该属性有值就更新,没有值不做处理。

UserMapper 接口

1/**
2     * 动态sql的set标签: 动态更新
3     * @param
4     * @return
5     */
6    public void updateIf(User user);

UserMapper 映射文件

 1<!-- 动态sql的set标签: 动态更新 -->
 2    <select id="updateIf" parameterType="user">
 3        update user
 4        <!-- <set>: 在更新时会自动添加set关键字,还会去掉最后一个条件的逗号 -->
 5        <set>
 6            <if test="usernameabc != null">
 7                username = #{usernameabc},
 8            </if>
 9            <if test="birthdayabc != null">
10                birthday = #{birthdayabc},
11            </if>
12            <if test="sexabc != null">
13                sex = #{sexabc},
14            </if>
15            <if test="addressabc != null">
16                address = #{addressabc},
17            </if>
18        </set>
19        where id = #{id}
20    </select>

测试类

 1/**
 2     *
 3     * 动态sql的set标签: 动态更新
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test11() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13
14        User user = new User();
15        user.setId(12);
16        user.setUsernameabc("开封第一猛男");
17        userMapper.updateIf(user);
18        sqlSession.commit();
19        sqlSession.close();
20    }

动态 SQL 之 foreach

foreach 主要是用来做数据的循环遍历
例如: select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分必须依靠foreach遍历才能实现。

1* <foreach>标签用于遍历集合,它的属性:
2	• collection:代表要遍历的集合元素
3	• open:代表语句的开始部分
4	• close:代表结束部分
5	• item:代表遍历集合的每个元素,生成的变量名
6	• sperator:代表分隔符
集合

UserMapper 接口

1/**
2     * 动态sql的foreach标签:根据多个id值查询用户(集合)
3     */
4    public List<User> findByList(List<Integer> ids);

UserMapper 映射文件

 1<!-- 动态sql的foreach标签(集合):根据多个id值查询用户
 2        List集合的别名: list
 3    -->
 4    <select id="findByList" parameterType="list" resultMap="userResultMap">
 5        select * from user
 6        <where>
 7            <!-- collection: 代表要遍历的集合元素,collection或list(list集合元素如果是基本数据类型的话)
 8                 open: 代表语句的开始部分
 9                 close: 代表语句的结束部分
10                 item: 代表遍历集合中的每一个元素
11                 separator: 分隔符
12            -->
13            <foreach collection="collection" open="id in (" close=")" item="element" separator=",">
14                #{element}
15            </foreach>
16        </where>
17    </select>

测试类

 1/**
 2     *
 3     * 动态sql的foreach标签(集合):根据多个id值查询用户
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test12() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        ArrayList<Integer> ids = new ArrayList<>();
14        ids.add(1);
15        ids.add(2);
16        ids.add(10);
17        ids.add(12);
18        List<User> users = userMapper.findByList(ids);
19        for (User user : users) {
20            System.out.println(user);
21//            User{id=1, usernameabc='子 慕', birthdayabc=Wed Nov 11 00:00:00 CST 2020, sexabc='男', addressabc='北京海淀'}
22//            User{id=2, usernameabc='应颠', birthdayabc=Sat Dec 12 00:00:00 CST 2020, sexabc='男', addressabc='北京海淀'}
23//            User{id=10, usernameabc='妞妞', birthdayabc=Fri Oct 28 15:09:52 CST 2022, sexabc='女', addressabc='河南开封'}
24//            User{id=12, usernameabc='开封第一猛男', birthdayabc=Fri Oct 28 16:44:54 CST 2022, sexabc='男', addressabc='河南开封'}
25        }
26        sqlSession.close();
27    }
数组

UserMapper 接口

1/**
2     * 动态sql的foreach标签:根据多个id值查询用户(数组)
3     */
4    public List<User> findByArray(Integer[] ids);

UserMapper 映射文件

 1<!-- 动态sql的foreach标签(数组):根据多个id值查询用户
 2        Integer别名: int
 3    -->
 4    <select id="findByArray" parameterType="int" resultMap="userResultMap">
 5        select * from user
 6        <where>
 7            <!-- collection: 代表要遍历的集合元素,collection或list(list集合元素如果是基本数据类型的话)
 8                 open: 代表语句的开始部分
 9                 close: 代表语句的结束部分
10                 item: 代表遍历集合中的每一个元素
11                 separator: 分隔符
12            -->
13            <foreach collection="array" open="id in (" close=")" item="element" separator=",">
14                #{element}
15            </foreach>
16        </where>
17    </select>

测试类

 1/**
 2     *
 3     * 动态sql的foreach标签(数组):根据多个id值查询用户
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test13() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13        Integer[] ids = {1,2,10,12};
14        List<User> users = userMapper.findByArray(ids);
15        for (User user : users) {
16            System.out.println(user);
17        }
18        sqlSession.close();
19    }

SQL 片段

用于抽取重复的 SQL 语句

UserMapper 映射文件

 1<!-- 抽取sql片段 -->
 2    <sql id="selectUser">
 3        select * from user
 4    </sql>
 5    
 6    <!-- 动态sql的foreach标签(集合):根据多个id值查询用户
 7        List集合的别名: list
 8    -->
 9    <select id="findByList" parameterType="list" resultMap="userResultMap">
10        <include refid="selectUser"/>
11        <where>
12            <!-- collection: 代表要遍历的集合元素,collection或list(list集合元素如果是基本数据类型的话)
13                 open: 代表语句的开始部分
14                 close: 代表语句的结束部分
15                 item: 代表遍历集合中的每一个元素
16                 separator: 分隔符
17            -->
18            <foreach collection="collection" open="id in (" close=")" item="element" separator=",">
19                #{element}
20            </foreach>
21        </where>
22    </select>
23
24    <!-- 动态sql的foreach标签(数组):根据多个id值查询用户
25        Integer别名: int
26    -->
27    <select id="findByArray" parameterType="int" resultMap="userResultMap">
28        <include refid="selectUser"/>
29        <where>
30            <!-- collection: 代表要遍历的集合元素,collection或list(list集合元素如果是基本数据类型的话)
31                 open: 代表语句的开始部分
32                 close: 代表语句的结束部分
33                 item: 代表遍历集合中的每一个元素
34                 separator: 分隔符
35            -->
36            <foreach collection="array" open="id in (" close=")" item="element" separator=",">
37                #{element}
38            </foreach>
39        </where>
40    </select>

核心配置文件

常用标签 功能
properties 加载外部 properties 文件
typeAliases 设置类型别名(MyBatis 默认设置了很多基本数据类型的别名,例如:Integer => int)
environments 配置数据源
plugins 引入第三方插件

plugins 标签

MyBatis 可以使用第三方的插件来对功能进行扩展。

PageHelper

分页助手 PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。

  1. 导入用于 PageHelper 依赖的 Maven 坐标
 1<!-- 分页助手 -->
 2        <dependency>
 3            <groupId>com.github.pagehelper</groupId>
 4            <artifactId>pagehelper</artifactId>
 5            <version>3.7.5</version>
 6        </dependency>
 7        <dependency>
 8            <groupId>com.github.jsqlparser</groupId>
 9            <artifactId>jsqlparser</artifactId>
10            <version>0.9.1</version>
11        </dependency>
  1. 在 MyBatis 核心配置文件中配置 PageHelper 插件(注意:<plugins>标签的位置在 properties、typeAliases 标签之后。)
1<!-- 引入第三方插件 -->
2    <plugins>
3        <!-- 分页助手的插件 -->
4        <plugin interceptor="com.github.pagehelper.PageHelper">
5            <!-- dialect(指定方言): 不同数据库分页语法不一样,mysql分页用limit -->
6            <property name="dialect" value="mysql"/>
7        </plugin>
8    </plugins>
  1. 测试分页
 1/**
 2     *
 3     * 测试分页PageHelper
 4     * @throws IOException
 5     */
 6    @Test
 7    public void Test14() throws IOException {
 8        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 9        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
10        SqlSession sqlSession = sqlSessionFactory.openSession();
11        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
12        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
13
14        //设置分页信息  startPage(pageNum,pageSize)
15        PageHelper.startPage(1, 2);
16
17        List<User> users = userMapper.findAllResultMap();
18        for (User user : users) {
19            System.out.println(user);
20        }
21
22        //获取分页相关信息
23        PageInfo<User> pageInfo = new PageInfo<User>(users);
24        System.out.println("总条数:"+pageInfo.getTotal());
25        System.out.println("总页数:"+pageInfo.getPages());
26        System.out.println("当前页:"+pageInfo.getPageNum());
27        System.out.println("每页显示长度:"+pageInfo.getPageSize());
28        System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
29        System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
30
31        //关闭数据库连接
32        sqlSession.close();
33    }

多表查询

数据关系及配置 总结
多对一(一对一)配置 使用 resultMap + assocication 做配置
一对多配置 使用 resultMap + collection 做配置
多对多 使用 resultMap + collection 做配置 (多对多的配置跟一对多很相似,难度在于 SQL 语句的编写)

实体之间关系

  • 一对一(人:身份证)
  • 一对多(用户:订单)
  • 多对多(学生:课程)

环境准备

 1DROP TABLE IF EXISTS `orders`;
 2CREATE TABLE `orders` (
 3`id` INT(11) NOT NULL AUTO_INCREMENT,
 4`ordertime` VARCHAR(255) DEFAULT NULL,
 5`total` DOUBLE DEFAULT NULL,
 6`uid` INT(11) DEFAULT NULL,
 7PRIMARY KEY (`id`),
 8KEY `uid` (`uid`),
 9CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
10) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
11-- ----------------------------
12-- Records of orders
13-- ----------------------------
14INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1');
15INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1');
16INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2');
17-- ----------------------------
18-- Table structure for sys_role
19-- ----------------------------
20DROP TABLE IF EXISTS `sys_role`;
21CREATE TABLE `sys_role` (
22`id` INT(11) NOT NULL AUTO_INCREMENT,
23`rolename` VARCHAR(255) DEFAULT NULL,
24`roleDesc` VARCHAR(255) DEFAULT NULL,
25PRIMARY KEY (`id`)
26) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
27-- ----------------------------
28-- Records of sys_role
29-- ----------------------------
30INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
31INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
32-- ----------------------------
33-- Table structure for sys_user_role
34-- ----------------------------
35DROP TABLE IF EXISTS `sys_user_role`;
36CREATE TABLE `sys_user_role` (
37`userid` INT(11) NOT NULL,
38`roleid` INT(11) NOT NULL,
39PRIMARY KEY (`userid`,`roleid`),
40KEY `roleid` (`roleid`),
41CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `sys_role`
42(`id`),
43CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `user`
44(`id`)
45) ENGINE=INNODB DEFAULT CHARSET=utf8;
46-- ----------------------------
47-- Records of sys_user_role
48-- ----------------------------
49INSERT INTO `sys_user_role` VALUES ('1', '1');
50INSERT INTO `sys_user_role` VALUES ('2', '1');
51INSERT INTO `sys_user_role` VALUES ('1', '2');
52INSERT INTO `sys_user_role` VALUES ('2', '2');

一对一

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询所有订单,与此同时查询出每个订单所属的用户

Bean
com.soulboy.domain.User

 1package com.soulboy.domain;
 2
 3import java.util.Date;
 4
 5public class User {
 6    private Integer id;
 7    private String username;
 8    private Date birthday;
 9    private String sex;
10    private String address;
11
12    @Override
13    public String toString() {
14        return "User{" +
15                "id=" + id +
16                ", username='" + username + '\'' +
17                ", birthday=" + birthday +
18                ", sex='" + sex + '\'' +
19                ", address='" + address + '\'' +
20                '}';
21    }
22
23    public Integer getId() {
24        return id;
25    }
26
27    public void setId(Integer id) {
28        this.id = id;
29    }
30
31    public String getUsername() {
32        return username;
33    }
34
35    public void setUsername(String username) {
36        this.username = username;
37    }
38
39    public Date getBirthday() {
40        return birthday;
41    }
42
43    public void setBirthday(Date birthday) {
44        this.birthday = birthday;
45    }
46
47    public String getSex() {
48        return sex;
49    }
50
51    public void setSex(String sex) {
52        this.sex = sex;
53    }
54
55    public String getAddress() {
56        return address;
57    }
58
59    public void setAddress(String address) {
60        this.address = address;
61    }
62}

com.soulboy.domain.Order

 1package com.soulboy.domain;
 2
 3public class Order {
 4    private Integer id;
 5    private String ordertime;
 6    private Double total;
 7    private Integer uid;
 8
 9    //表示当前订单属于哪个用户
10    private User user;
11
12
13    @Override
14    public String toString() {
15        return "Order{" +
16                "id=" + id +
17                ", ordertime='" + ordertime + '\'' +
18                ", total=" + total +
19                ", uid=" + uid +
20                ", user=" + user +
21                '}';
22    }
23
24    public Integer getId() {
25        return id;
26    }
27
28    public void setId(Integer id) {
29        this.id = id;
30    }
31
32    public String getOrdertime() {
33        return ordertime;
34    }
35
36    public void setOrdertime(String ordertime) {
37        this.ordertime = ordertime;
38    }
39
40    public Double getTotal() {
41        return total;
42    }
43
44    public void setTotal(Double total) {
45        this.total = total;
46    }
47
48    public Integer getUid() {
49        return uid;
50    }
51
52    public void setUid(Integer uid) {
53        this.uid = uid;
54    }
55
56    public User getUser() {
57        return user;
58    }
59
60    public void setUser(User user) {
61        this.user = user;
62    }
63}

UserMapper 接口

 1package com.soulboy.mapper;
 2
 3import com.soulboy.domain.Order;
 4
 5import java.util.List;
 6
 7public interface OrderMapper {
 8    /*
 9        一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息
10     */
11    public List<Order> findAllOrderWithUser();
12}

OrderMapper 映射文件

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.OrderMapper">
 7    <!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装
 8        id: 标签的唯一标识
 9        type: 封装后实体类型
10        property:实体的属性名
11        column: 表中字段的属性名
12        <id>: 主键的封装
13        <result>: 表中普通字段的封装
14     -->
15    <resultMap id="orderMap" type="com.soulboy.domain.Order">
16        <id property="id" column="id"></id>
17        <result property="ordertime" column="ordertime"></result>
18        <result property="total" column="total"></result>
19        <result property="uid" column="uid"></result>
20        <!-- association:在进行一对一关联查询配置时,使用association标签进行关联
21                property: 实体的属性名称
22                javaType: 属性类型
23        -->
24        <association property="user" javaType="com.soulboy.domain.User">
25            <id property="id" column="uid"/>
26            <result property="username" column="username"/>
27            <result property="birthday" column="birthday"/>
28            <result property="sex" column="sex"/>
29            <result property="address" column="address"/>
30        </association>
31    </resultMap>
32
33    <!-- 一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息 -->
34    <select id="findAllOrderWithUser" resultMap="orderMap">
35        select * from orders,user where orders.uid = user.id
36        <!-- select * from orders o left join user u on o.uid = u.id -->
37    </select>
38
39</mapper>

测试类

 1package com.soulboy.test;
 2
 3
 4import com.soulboy.domain.Order;
 5import com.soulboy.mapper.OrderMapper;
 6import org.apache.ibatis.io.Resources;
 7import org.apache.ibatis.session.SqlSession;
 8import org.apache.ibatis.session.SqlSessionFactory;
 9import org.apache.ibatis.session.SqlSessionFactoryBuilder;
10import org.junit.Test;
11import java.io.IOException;
12import java.io.InputStream;
13import java.util.List;
14
15public class MybatisTest {
16    /**
17     * 一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息
18    */
19    @Test
20    public void Test1() throws IOException {
21        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
22        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
23        SqlSession sqlSession = sqlSessionFactory.openSession();
24        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
25        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
26        List<Order> orders = orderMapper.findAllOrderWithUser();
27        for (Order order : orders) {
28            System.out.println(order);
29        }
30        sqlSession.close();
31    }
32}

一对多

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单

select * from orders,user where orders.uid = user.id
select u.*,o.id as oid,o.ordertime,o.uid from orders o right join user u on o.uid = u.id

Bean
com.soulboy.domain.User

 1package com.soulboy.domain;
 2
 3import java.util.Date;
 4import java.util.List;
 5
 6public class User {
 7    private Integer id;
 8    private String username;
 9    private Date birthday;
10    private String sex;
11    private String address;
12
13    //表示多方关系: 集合
14    private List<Order> ordersList;
15
16    @Override
17    public String toString() {
18        return "User{" +
19                "id=" + id +
20                ", username='" + username + '\'' +
21                ", birthday=" + birthday +
22                ", sex='" + sex + '\'' +
23                ", address='" + address + '\'' +
24                ", ordersList=" + ordersList +
25                '}';
26    }
27
28    public Integer getId() {
29        return id;
30    }
31
32    public void setId(Integer id) {
33        this.id = id;
34    }
35
36    public String getUsername() {
37        return username;
38    }
39
40    public void setUsername(String username) {
41        this.username = username;
42    }
43
44    public Date getBirthday() {
45        return birthday;
46    }
47
48    public void setBirthday(Date birthday) {
49        this.birthday = birthday;
50    }
51
52    public String getSex() {
53        return sex;
54    }
55
56    public void setSex(String sex) {
57        this.sex = sex;
58    }
59
60    public String getAddress() {
61        return address;
62    }
63
64    public void setAddress(String address) {
65        this.address = address;
66    }
67
68    public List<Order> getOrdersList() {
69        return ordersList;
70    }
71
72    public void setOrdersList(List<Order> ordersList) {
73        this.ordersList = ordersList;
74    }
75}

com.soulboy.domain.Order

 1package com.soulboy.domain;
 2
 3import java.util.Date;
 4import java.util.List;
 5
 6public class User {
 7    private Integer id;
 8    private String username;
 9    private Date birthday;
10    private String sex;
11    private String address;
12
13    //表示多方关系: 集合
14    private List<Order> ordersList;
15
16    @Override
17    public String toString() {
18        return "User{" +
19                "id=" + id +
20                ", username='" + username + '\'' +
21                ", birthday=" + birthday +
22                ", sex='" + sex + '\'' +
23                ", address='" + address + '\'' +
24                ", ordersList=" + ordersList +
25                '}';
26    }
27
28    public Integer getId() {
29        return id;
30    }
31
32    public void setId(Integer id) {
33        this.id = id;
34    }
35
36    public String getUsername() {
37        return username;
38    }
39
40    public void setUsername(String username) {
41        this.username = username;
42    }
43
44    public Date getBirthday() {
45        return birthday;
46    }
47
48    public void setBirthday(Date birthday) {
49        this.birthday = birthday;
50    }
51
52    public String getSex() {
53        return sex;
54    }
55
56    public void setSex(String sex) {
57        this.sex = sex;
58    }
59
60    public String getAddress() {
61        return address;
62    }
63
64    public void setAddress(String address) {
65        this.address = address;
66    }
67
68    public List<Order> getOrdersList() {
69        return ordersList;
70    }
71
72    public void setOrdersList(List<Order> ordersList) {
73        this.ordersList = ordersList;
74    }
75}

com.soulboy.mapper.UserMapper

 1package com.soulboy.mapper;
 2
 3
 4import com.soulboy.domain.User;
 5import java.util.List;
 6
 7public interface UserMapper {
 8    /*
 9        一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单
10     */
11    public List<User> findAllUserWithOrder();
12}

com/soulboy/mapper/UserMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7    <!-- resultMap:手动配置实体属性与表中字段的映射关系,完成手动封装
 8        id: 标签的唯一标识
 9        type: 封装后实体类型
10        property:实体的属性名
11        column: 表中字段的属性名
12        <id>: 主键的封装
13        <result>: 表中普通字段的封装
14     -->
15    <resultMap id="userMap" type="com.soulboy.domain.User">
16        <id property="id" column="id"></id>
17        <result property="username" column="username"></result>
18        <result property="birthday" column="birthday"></result>
19        <result property="sex" column="sex"></result>
20        <result property="address" column="address"></result>
21        <!-- collection:一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单
22                property: 实体的属性名称
23                javaType: 属性类型
24        -->
25        <collection property="ordersList" ofType="com.soulboy.domain.Order">
26            <id property="id" column="oid"></id>
27            <result property="ordertime" column="ordertime"/>
28            <result property="total" column="total"/>
29            <result property="uid" column="uid"/>
30        </collection>
31    </resultMap>
32    <!-- 一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单 -->
33    <select id="findAllUserWithOrder" resultMap="userMap">
34        select u.*,o.id oid,o.ordertime,o.total,o.uid from orders o right join user u on o.uid = u.id
35        <!-- select * from orders o left join user u on o.uid = u.id -->
36    </select>
37</mapper>

测试类

 1/**
 2     * 一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单
 3     */
 4    @Test
 5    public void Test2() throws IOException {
 6        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 7        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 8        SqlSession sqlSession = sqlSessionFactory.openSession();
 9        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
10        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
11        List<User> users = userMapper.findAllUserWithOrder();
12        for (User user : users) {
13            System.out.println(user);
14        }
15        sqlSession.close();
16//        User{id=1, username='子 慕', birthday=Wed Nov 11 00:00:00 CST 2020, sex='男', address='北京海淀', ordersList=[Order{id=1, ordertime='2020-12-12', total=3000.0, uid=1, user=null}, Order{id=2, ordertime='2020-12-12', total=4000.0, uid=1, user=null}]}
17//        User{id=2, username='应颠', birthday=Sat Dec 12 00:00:00 CST 2020, sex='男', address='北京海淀', ordersList=[Order{id=3, ordertime='2020-12-12', total=5000.0, uid=2, user=null}]}
18//        User{id=5, username='高中美', birthday=Sun Oct 23 18:04:52 CST 2022, sex='女', address='河南开封', ordersList=[]}
19//        User{id=8, username='高中雅', birthday=Fri Oct 28 15:07:50 CST 2022, sex='女', address='河南开封', ordersList=[]}
20//        User{id=10, username='妞妞', birthday=Fri Oct 28 15:09:52 CST 2022, sex='女', address='河南开封', ordersList=[]}
21//        User{id=11, username='超蛋', birthday=Fri Oct 28 16:39:31 CST 2022, sex='男', address='河南开封', ordersList=[]}
22//        User{id=12, username='开封第一猛男', birthday=Fri Oct 28 16:44:54 CST 2022, sex='男', address='河南开封', ordersList=[]}
23    }

多对多

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询所有用户同时查询出该用户的所有角色

SELECT u.*,r.id AS rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur On ur.userid=u.id LEFT JOIN sys_role r ON ur.roleid=r.id;

Bean (多对多任何一段的角度都可以,这里选择的是 User)

com.soulboy.domain.User

 1package com.soulboy.domain;
 2
 3import java.util.Date;
 4import java.util.List;
 5
 6public class User {
 7    private Integer id;
 8    private String username;
 9    private Date birthday;
10    private String sex;
11    private String address;
12
13    // 表示多方关系(一对多): 代表了当前用户所具有的订单列表 collection
14    private List<Order> ordersList;
15
16    // 表示多方关系(多对多): 代表了当前用户所具有的角色列表 collection
17    private List<Role> roleList;
18
19
20    public Integer getId() {
21        return id;
22    }
23
24    public void setId(Integer id) {
25        this.id = id;
26    }
27
28    public String getUsername() {
29        return username;
30    }
31
32    public void setUsername(String username) {
33        this.username = username;
34    }
35
36    public Date getBirthday() {
37        return birthday;
38    }
39
40    public void setBirthday(Date birthday) {
41        this.birthday = birthday;
42    }
43
44    public String getSex() {
45        return sex;
46    }
47
48    public void setSex(String sex) {
49        this.sex = sex;
50    }
51
52    public String getAddress() {
53        return address;
54    }
55
56    public void setAddress(String address) {
57        this.address = address;
58    }
59
60    public List<Order> getOrdersList() {
61        return ordersList;
62    }
63
64    public void setOrdersList(List<Order> ordersList) {
65        this.ordersList = ordersList;
66    }
67
68    public List<Role> getRoleList() {
69        return roleList;
70    }
71
72    public void setRoleList(List<Role> roleList) {
73        this.roleList = roleList;
74    }
75
76    @Override
77    public String toString() {
78        return "User{" +
79                "id=" + id +
80                ", username='" + username + '\'' +
81                ", birthday=" + birthday +
82                ", sex='" + sex + '\'' +
83                ", address='" + address + '\'' +
84                ", ordersList=" + ordersList +
85                ", roleList=" + roleList +
86                '}';
87    }
88}

com.soulboy.domain.Role

 1package com.soulboy.domain;
 2
 3public class Role {
 4    private Integer id;
 5    private String rolename;
 6    private String roleDesc;
 7
 8    @Override
 9    public String toString() {
10        return "Role{" +
11                "id=" + id +
12                ", rolename='" + rolename + '\'' +
13                ", roleDesc='" + roleDesc + '\'' +
14                '}';
15    }
16
17    public Integer getId() {
18        return id;
19    }
20
21    public void setId(Integer id) {
22        this.id = id;
23    }
24
25    public String getRolename() {
26        return rolename;
27    }
28
29    public void setRolename(String rolename) {
30        this.rolename = rolename;
31    }
32
33    public String getRoleDesc() {
34        return roleDesc;
35    }
36
37    public void setRoleDesc(String roleDesc) {
38        this.roleDesc = roleDesc;
39    }
40}

UserMapper 接口

 1package com.soulboy.mapper;
 2
 3
 4import com.soulboy.domain.User;
 5import java.util.List;
 6
 7public interface UserMapper {
 8
 9    /*
10        多对多关联查询:查询所有用户,同时还要查询出每个用户所关联的角色信息
11    */
12    public List<User> findAllUserWithRole();
13
14
15}

UserMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7    <!-- 多对多关联查询:查询所有用户,同时还要查询出每个用户所关联的角色信息 -->
 8    <resultMap id="userRoleMap" type="com.soulboy.domain.User">
 9        <id property="id" column="id"/>
10        <result property="username" column="username"></result>
11        <result property="birthday" column="birthday"></result>
12        <result property="sex" column="sex"></result>
13        <result property="address" column="address"></result>
14        <collection property="roleList" ofType="com.soulboy.domain.Role">
15            <id property="id" column="rid"></id>
16            <result property="rolename" column="rolename"></result>
17            <result property="roleDesc" column="roleDesc"></result>
18        </collection>
19    </resultMap>
20    <select id="findAllUserWithRole" resultMap="userRoleMap">
21        SELECT u.*,r.id AS rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur On ur.userid=u.id LEFT JOIN sys_role r ON ur.roleid=r.id
22    </select>
23</mapper>

测试类

 1/**
 2     * 多对多关联查询:查询所有用户,同时还要查询出每个用户所关联的角色信息
 3     */
 4    @Test
 5    public void Test3() throws IOException {
 6        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 7        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 8        SqlSession sqlSession = sqlSessionFactory.openSession();
 9        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
10        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
11        List<User> users = userMapper.findAllUserWithRole();
12
13        for (User user : users) {
14            System.out.println(user);
15        }
16//        User{id=1, username='子 慕', birthday=Wed Nov 11 00:00:00 CST 2020, sex='男', address='北京海淀', ordersList=null, roleList=[Role{id=1, rolename='CTO', roleDesc='CTO'}, Role{id=2, rolename='CEO', roleDesc='CEO'}]}
17//        User{id=2, username='应颠', birthday=Sat Dec 12 00:00:00 CST 2020, sex='男', address='北京海淀', ordersList=null, roleList=[Role{id=1, rolename='CTO', roleDesc='CTO'}, Role{id=2, rolename='CEO', roleDesc='CEO'}]}
18//        User{id=5, username='高中美', birthday=Sun Oct 23 18:04:52 CST 2022, sex='女', address='河南开封', ordersList=null, roleList=[]}
19//        User{id=8, username='高中雅', birthday=Fri Oct 28 15:07:50 CST 2022, sex='女', address='河南开封', ordersList=null, roleList=[]}
20//        User{id=10, username='妞妞', birthday=Fri Oct 28 15:09:52 CST 2022, sex='女', address='河南开封', ordersList=null, roleList=[]}
21//        User{id=11, username='超蛋', birthday=Fri Oct 28 16:39:31 CST 2022, sex='男', address='河南开封', ordersList=null, roleList=[]}
22//        User{id=12, username='开封第一猛男', birthday=Fri Oct 28 16:44:54 CST 2022, sex='男', address='河南开封', ordersList=null, roleList=[]}
23    }

加载策略

实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息,因为一个用户可能会关联多个订单,此时就需要用到延迟加载。延迟加载是基于嵌套查询来实现的

加载策略 数据关系 优缺点
延迟加载 一对多,多对多 优点:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。 缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
立即加载 一对一,多对一
1* 在一对多中,当我们有一个用户,它有个100个订单
2	在查询用户的时候,要不要把关联的订单查出来?
3	在查询订单的时候,要不要把关联的用户查出来?
4
5* 回答
6	在查询用户时,用户下的订单应该是,什么时候用,什么时候查询。
7	在查询订单时,订单所属的用户信息应该是随着订单一起查询出来。

局部延迟加载

在 association 和 collection 标签中都有一个 fetchType 属性,通过修改它的值,可以修改局部的加载策略

设置触发延迟加载的方法
在配置了延迟加载策略后,发现即使没有调用关联对象的任何方法,但是在你调用当前对象的 equals、clone、hashCode、toString 方法时也会触发关联对象的查询。
我们可以在配置文件中使用 lazyLoadTriggerMethods 配置项覆盖掉上面四个方法。
sqlMapConfig.xml

 1<configuration>
 2    <properties/>
 3
 4    <!-- 设置触发延迟加载的方法 -->
 5    <settings>
 6        <!--所有方法都会延迟加载-->
 7        <setting name="lazyLoadTriggerMethods" value="toString()"/>
 8    </settings>
 9
10    <typeAliases/>
11    <plugins/>
12    <environments/>
13    <mappers/>
14</configuration>

需求:一对多嵌套查询(延迟加载):查询所有的用户,同时还要查询出每个用户所关联的订单信息

com.soulboy.mapper.UserMapper

1public interface UserMapper {
2    一对多查询的需求(嵌套查询):查询所有用户,与此同时查询出该用户具有的订单
3     */
4    public List<User> findAllUserWithOrder2();
5}

UserMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7
 8    <!-- 一对多查询的需求(嵌套查询实现延迟加载):查询所有用户,与此同时查询出该用户具有的订单 -->
 9    <resultMap id="userOrderMap2" type="com.soulboy.domain.User">
10        <id property="id" column="id"></id>
11        <result property="username" column="username"></result>
12        <result property="birthday" column="birthday"></result>
13        <result property="sex" column="sex"></result>
14        <result property="address" column="address"></result>
15        <!-- collection:一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单
16                property: 实体的属性名称
17                javaType: 属性类型
18                id: 传入select查询的参数
19                fetchType="lazy" : 延迟加载策略
20                fetchType="eager": 立即加载策略
21        -->
22        <collection property="ordersList" 
23		    ofType="com.soulboy.domain.Order"
24                    column="id" 
25		    select="com.soulboy.mapper.OrderMapper.findByUid"
26                    fetchType="lazy"></collection>
27    </resultMap>
28
29    <!-- 一对多查询的需求(嵌套查询实现延迟加载):查询所有用户,与此同时查询出该用户具有的订单 -->
30    <select id="findAllUserWithOrder2" resultMap="userOrderMap2">
31        SELECT * FROM USER
32    </select>
33
34</mapper>

com.soulboy.mapper.OrderMapper

1public interface OrderMapper {
2    /**
3     * 根据用户id查询订单
4     */
5    public List<Order>  findByUid(Integer uid);
6}

OrderMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.OrderMapper">
 7
 8    <!-- 根据uid查询订单 -->
 9    <select id="findByUid" parameterType="int" resultType="com.soulboy.domain.Order">
10        SELECT * FROM orders WHERE uid = #{uid}
11    </select>
12
13</mapper>

实现类

 1/**
 2     * 一对多查询的需求(嵌套查询实现延迟加载):查询所有用户,与此同时查询出该用户具有的订单
 3     */
 4    @Test
 5    public void Test5() throws IOException {
 6        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 7        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 8        SqlSession sqlSession = sqlSessionFactory.openSession();
 9        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
10        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
11        List<User> users = userMapper.findAllUserWithOrder2();
12        for (User user : users) {
13            System.out.println(user);
14        }
15        sqlSession.close();
16    }

全局延迟加载

在 MyBatis 的核心配置文件中可以使用 setting 标签修改全局的加载策略。注意:局部的加载策略优先级高于全局的加载策略。

sqlMapConfig.xml

1<!-- 设置触发延迟加载的方法 -->
2    <settings>
3        <!--开启全局延迟加载功能-->
4        <setting name="lazyLoadingEnabled" value="true"/>
5        <!--所有方法都会延迟加载-->
6        <setting name="lazyLoadTriggerMethods" value="toString()"/>
7    </settings>

缓存

 当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。
 一句话概括:经常查询一些不经常发生变化的数据,使用缓存来提高查询效率。
 像大多数的持久化框架一样,MyBatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 MyBatis 中缓存分为一级缓存,二级缓存。

  1. MyBatis 的缓存,都不需要我们手动存储和获取数据。MyBatis 自动维护的。
  2. MyBatis 开启了二级缓存后,那么查询顺序:二级缓存--》一级缓存--》数据库
  3. 注意:MyBatis 的二级缓存会存在脏读问题,需要使用第三方的缓存技术解决问题。

一级缓存

原理示意图

 一级缓存是 SqlSession 级别的缓存,是默认开启的。
 所以在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用一个 Mapper 方法,往往只执行一次 SQL,因为使用 SelSession 第一次查询后,MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
一级缓存示意图

生命周期

 一级缓存是 SqlSession 范围的缓存,执行 SqlSession 的 C(增加)U(更新)D(删除)操作,或者调用 clearCache()、commit()、close()方法,都会清空缓存。
生命周期

11.第一次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,如果没有,从数据库查询用户信息。
22.得到用户信息,将用户信息存储到一级缓存中。
33.如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
44.第二次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,缓存中有,直接从缓存中获取用户信息。

清除缓存

在映射文件中的 select 标签中使用 flushCache 属性

1<!-- 根据id查询用户:每次查询时,都会清除缓存 -->
2    <select id="findById" resultType="com.soulboy.domain.User" parameterType="int" flushCache="true">
3        select * from user where id = #{id}
4    </select>

手动清除一级缓存

 1/**
 2     * 验证mybatis中的一级缓存
 3     */
 4    @Test
 5    public void testLevelOneCache() throws IOException {
 6        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 7        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 8        SqlSession sqlSession = sqlSessionFactory.openSession();
 9        //当前返回  其实是基于UserMapper所产生的代理对象(底层JDK动态代理:proxy)
10        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
11        //根据id查询用户信息第,第一次查询是数据库
12        User user = userMapper.findById(1);
13        System.out.println(user);
14
15        //清除一级缓存
16        sqlSession.clearCache();
17        
18        //第二次查询通过观察log4j控制台输出,并没有发现有: Preparing: select * from user where id = ?
19        user = userMapper.findById(1);
20        System.out.println(user);
21        sqlSession.close();
22    }

二级缓存

 二级缓存是 namspace 级别(跨 sqlSession)的缓存,是默认不开启的
 二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis 要求返回的 POJO 必须是可序列化的。也就是要求实现 Serializable 接口,配置方法很简单,只需要在映射 XML 文件配置就可以开启二级缓存了。
二级缓存

验证流程
配置核心配置文件 sqlMapConfig.xml

 1<settings>
 2        <!--开启全局延迟加载功能-->
 3        <setting name="lazyLoadingEnabled" value="true"/>
 4        <!--所有方法都会延迟加载-->
 5        <setting name="lazyLoadTriggerMethods" value="toString()"/>
 6        <!-- 因为cacheEnabled的取值默认就为true,
 7        所以这一步可以省略不配置。为true代表开启二级缓存;
 8        为false代表不开启二级缓存。 -->
 9        <setting name="cacheEnabled" value="true"/>
10    </settings>

修改 User 实现 Serializable 接口

1public class User implements Serializable {
2    private Integer id;
3    private String username;
4    private Date birthday;
5    private String sex;
6    private String address;
7

配置映射文件 UerMapper.xml

 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5
 6<mapper namespace="com.soulboy.mapper.UserMapper">
 7    <!--当前映射文件开启二级缓存-->
 8    <cache></cache>
 9    <!-- 根据id查询用户,每次查询时,都会清除缓存-->
10    <select id="findById" parameterType="int" resultType="com.soulboy.domain.User" useCache="true">
11        select * from user where id = #{id}
12    </select>
13</mapper>

测试类

 1/**
 2     * 验证mybatis中的一级缓存
 3     */
 4    @Test
 5    public void testTwoCache() throws IOException {
 6        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 7        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
 8
 9        //SqlSession1
10        SqlSession sqlSession1 = sqlSessionFactory.openSession();
11        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
12        User user1 = userMapper1.findById(1);
13        System.out.println(user1);
14        //只有执行sqlSession.commit或者sqlSession.close方法,一级缓存中的数据才会被刷新到二级缓存中
15        sqlSession1.close();
16        
17        //SqlSession2
18        SqlSession sqlSession2 = sqlSessionFactory.openSession();
19        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
20        User user2 = userMapper2.findById(1);    //12:00:39,294 DEBUG UserMapper:60 - Cache Hit Ratio [com.soulboy.mapper.UserMapper]: 0.5
21
22        System.out.println(user2);
23        sqlSession2.close();
24    }

二级缓存会引发脏读

实际开发中不会使用二级缓存,MyBatis 的二级缓存因为是 namespace 级别,所以在进行多表查询时会产生脏读问题


作者:Soulboy