Mybatis延迟加载、缓存

一、延迟加载

java项目目录结构

1、一对一延迟加载

1)创建User和Account实体类(具体代码参见我的之前博客)

2)创建UserDao接口和AccountDao接口

  UserDao接口:

 1 package sun.dao;
 2 
 3 import sun.domain.User;
 4 
 5 import java.util.List;
 6 
 7 public interface UserDao {
 8     /**
 9      * 查询所有用户和该用户名下的账户信息
10      * @return
11      */
12     List<User> findAll();
13 
14     /**
15      * 根据id查找用户信息
16      * @param id
17      * @return
18      */
19     User findUserById(Integer id);
20 
21 }
UserDao接口

  AccountDao接口:

 1 package sun.dao;
 2 
 3 import sun.domain.Account;
 4 
 5 import java.util.List;
 6 
 7 /**
 8  * @Classname AccountDao
 9  * @Description TODO
10  * @Date 2020/9/11 13:13
11  * @Created by Administrator
12  */
13 public interface AccountDao {
14     /**
15      * 查询所有账户信息和该账户的所属者
16      */
17     List<Account> findAll();
18 
19     /**
20      * 根据id查找账户信息
21      * @param id
22      * @return
23      */
24     Account findAccountById(Integer id);
25 
26 }
AccountDao接口

3)创建AccountDao.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="sun.dao.AccountDao">
 7     <!--封装Account类-->
 8     <resultMap id="accountUserMap" type="account">
 9         <id property="id" column="id"></id>
10         <result property="uid" column="uid"></result>
11         <result property="money" column="money"></result>
12       
13         <!--延迟加载-->
14         <association property="user" column="uid" javaType="user" select="sun.dao.UserDao.findUserById"></association>
15     </resultMap>
16 
17     <!--查询所有-->
18     <select id="findAll" resultMap="accountUserMap">
19         SELECT * from account
20     </select>
21 <?xml version="1.0" encoding="UTF-8"?>
22 <!DOCTYPE mapper
23         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
24         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
25 
26 <mapper namespace="sun.dao.AccountDao">
27     <!--封装Account类-->
28     <resultMap id="accountUserMap" type="account">
29         <id property="id" column="id"></id>
30         <result property="uid" column="uid"></result>
31         <result property="money" column="money"></result>
32 
33         <!--延迟加载 select表示延时加载时执行的操作-->
34         <association property="user" column="uid" javaType="user" select="sun.dao.UserDao.findUserById"></association>
35     </resultMap>
36 
37     <!--查询所有-->
38     <select id="findAll" resultMap="accountUserMap">
39         SELECT * from account
40     </select>
41     <!--根据id查询账户-->
42     <select id="findAccountById" resultType="account">
43         SELECT * from account where uid=#{id}
44     </select>
45 
46 </mapper>
47     <select id="findAccountById" resultType="account">
48         SELECT * from account where uid=#{id}
49     </select>
50 
51 </mapper>
52     <select id="findAccountById" resultType="account">
53         SELECT * from account where uid=#{id}
54     </select>
55 
56 </mapper>
AccountDao.xml

4)在主配置文件中配置延时加载设置

 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 
 7 <!--mybatis主配置文件-->
 8 <configuration>
 9     
10     <properties resource="jdbcConfig.properties"></properties>
11 
12     <!--设置延时加载-->
13     <settings>
14         <setting name="lazyLoadingEnabled" value="true"/>
15         <setting name="aggressiveLazyLoading" value="false"/>
16     </settings>
17 
18     <typeAliases>
19         <package name="sun.domain"></package>
20     </typeAliases>
21 
22     <!--配置环境-->
23     <environments default="mysql">
24         <!--配置mysql环境-->
25         <environment id="mysql">
26             <!--配置事务类型-->
27             <transactionManager type="JDBC"></transactionManager>
28             <!--配置数据库连接池-->
29             <dataSource type="POOLED">
30                 <!--配置连接数据库的四个基本信息-->
31                 <property name="driver" value="${jdbc.driver}"></property>
32                 <property name="url" value="${jdbc.url}"></property>
33                 <property name="username" value="${jdbc.username}"></property>
34                 <property name="password" value="${jdbc.password}"></property>
35             </dataSource>
36 
37 
38         </environment>
39     </environments>
40 
41     <!--指定映射配置文件位置-->
42     <mappers>
43         <!--<mapper resource="sun/dao/UserDao.xml"></mapper>-->
44 
45         <!--package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class-->
46         <package name="sun.dao"></package>
47     </mappers>
48 </configuration>
配置主配置文件延时加载设置

5)测试查询账户时延时加载用户是否生效

 1 package sun.test;
 2 
 3 
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.junit.After;
 9 import org.junit.Before;
10 import org.junit.Test;
11 import sun.dao.AccountDao;
12 import sun.domain.Account;
13 
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.List;
17 
18 public class AccountTest {
19 
20     private InputStream in;
21     private SqlSession sqlSession;
22     private AccountDao accountDao;
23 
24     @Before
25     public void init() throws IOException {
26         // 读取配置文件
27         in = Resources.getResourceAsStream("SqlMapConfig.xml");
28         // 创建SqlSessionFactory
29         SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
30         SqlSessionFactory factory = builder.build(in);
31         // 使用工厂生产sqlsession对象
32         sqlSession = factory.openSession();
33         // 使用sqlsession创建UserDao接口代理对象
34         accountDao = sqlSession.getMapper(AccountDao.class);
35     }
36 
37     @After
38     public void destory() throws IOException {
39         sqlSession.commit();
40         sqlSession.close();
41         in.close();
42     }
43 
44     @Test
45     public void findAllTest() {
46         // 使用代理对象执行方法
47         List<Account> all = accountDao.findAll();
48         for (Account account : all) {
49             System.out.println("----------------");
50             System.out.println(account);
51             System.out.println(account.getUser());
52         }
53     }
54 
55 }
测试类

测试结果:

 2、一对多延时加载

1)同上

2)同上

3)创建UserDao.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="sun.dao.UserDao">
 7     <resultMap id="userAccountMap" type="user">
 8         <id property="id" column="id"></id>
 9         <result property="username" column="username"></result>
10         <result property="sex" column="sex"></result>
11         <result property="birthday" column="birthday"></result>
12         <result property="address" column="address"></result>
13         <!--封装用户名下的账户信息-->
14         <!--延迟加载-->
15         <collection property="accounts" ofType="account" select="sun.dao.AccountDao.findAccountById" column="id"></collection>
16     </resultMap>
17 
18     <!--查询所有-->
19     <select id="findAll" resultMap="userAccountMap">
20         SELECT * from user
21     </select>
22 
23     <!--查询指定id用户-->
24     <select id="findUserById" resultType="user">
25         SELECT * from user where id=#{id}
26     </select>
27 </mapper>
UserDao.xml

4)同上

5)测试类

 1 package sun.test;
 2 
 3 
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.junit.After;
 9 import org.junit.Before;
10 import org.junit.Test;
11 import sun.dao.UserDao;
12 import sun.domain.User;
13 
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.Date;
17 import java.util.List;
18 
19 public class UserTest {
20 
21     private InputStream in;
22     private SqlSession sqlSession;
23     private UserDao userDao;
24 
25     @Before
26     public void init() throws IOException {
27         // 读取配置文件
28         in = Resources.getResourceAsStream("SqlMapConfig.xml");
29         // 创建SqlSessionFactory
30         SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
31         SqlSessionFactory factory = builder.build(in);
32         // 使用工厂生产sqlsession对象
33         sqlSession = factory.openSession();
34         // 使用sqlsession创建UserDao接口代理对象
35         userDao = sqlSession.getMapper(UserDao.class);
36     }
37 
38     @After
39     public void destory() throws IOException {
40         sqlSession.commit();
41         sqlSession.close();
42         in.close();
43     }
44 
45     @Test
46     public void findAllTest() {
47         // 使用代理对象执行方法
48         List<User> all = userDao.findAll();
49         for (User user : all) {
50             System.out.println("---------------");
51             System.out.println(user);
52             System.out.println(user.getAccounts());
53         }
54     }
55 
56 }
测试类

测试结果:

 二、缓存

java项目目录结构

1、一级缓存

概述:一级缓存指的是sqlSession对象的缓存。当第一次查询之后,查询结果会同时存入sqlSession为我们提供的一块区域中,该区域的结构是一个HashMap集合,当我们再次查询相同数据时,mybatis会先去该区域查询是否存在,如果存在直接获取。当sqlSession对象消失,一级缓存也就不存在了。一级缓存默认是开启的。

原理图:

1)在domain包下创建User实体类

 1 package sun.domain;
 2 
 3 import java.io.Serializable;
 4 import java.util.Date;
 5 import java.util.List;
 6 
 7 public class User implements Serializable {
 8     private Integer id;
 9     private String username;
10     private Date birthday;
11     private String sex;
12     private String address;
13 
14     public Integer getId() {
15         return id;
16     }
17 
18     public void setId(Integer id) {
19         this.id = id;
20     }
21 
22     public String getUsername() {
23         return username;
24     }
25 
26     public void setUsername(String username) {
27         this.username = username;
28     }
29 
30     public Date getBirthday() {
31         return birthday;
32     }
33 
34     public void setBirthday(Date birthday) {
35         this.birthday = birthday;
36     }
37 
38     public String getSex() {
39         return sex;
40     }
41 
42     public void setSex(String sex) {
43         this.sex = sex;
44     }
45 
46     public String getAddress() {
47         return address;
48     }
49 
50     public void setAddress(String address) {
51         this.address = address;
52     }
53 
54 }
User实体类

2)在dao包下创建UserDao接口

 1 package sun.dao;
 2 
 3 import sun.domain.User;
 4 
 5 import java.util.List;
 6 
 7 public interface UserDao {
 8     /**
 9      * 查询所有用户和该用户名下的账户信息
10      * @return
11      */
12     List<User> findAll();
13 
14     /**
15      * 根据id查询用户
16      * @param id
17      * @return
18      */
19     User findUserById(Integer id);
20 
21 }
UserDao接口

3)测试进行相同的两次查询时获取对象是否是同一个

 1 package sun.test;
 2 
 3 
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.junit.After;
 9 import org.junit.Before;
10 import org.junit.Test;
11 import sun.dao.UserDao;
12 import sun.domain.User;
13 
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.Date;
17 import java.util.List;
18 
19 public class firstLevelCacheTest {
20 
21     private InputStream in;
22     private SqlSession sqlSession;
23     private UserDao userDao;
24 
25     @Before
26     public void init() throws IOException {
27         // 读取配置文件
28         in = Resources.getResourceAsStream("SqlMapConfig.xml");
29         // 创建SqlSessionFactory
30         SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
31         SqlSessionFactory factory = builder.build(in);
32         // 使用工厂生产sqlsession对象
33         sqlSession = factory.openSession();
34         // 使用sqlsession创建UserDao接口代理对象
35         userDao = sqlSession.getMapper(UserDao.class);
36     }
37 
38     @After
39     public void destory() throws IOException {
40         sqlSession.commit();
41         sqlSession.close();
42         in.close();
43     }
44 
45     @Test
46     public void findOneTest(){
47         User user1 = userDao.findUserById(41);
48         System.out.println(user1);
49         User user2 = userDao.findUserById(41);
50         System.out.println(user2);
51         System.out.println(user1==user2);
52     }
53 
54 }
测试类

测试结果:

 4)调用sqlSession对象的clearCache方法清理一级缓存

 1 package sun.test;
 2 
 3 
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.junit.After;
 9 import org.junit.Before;
10 import org.junit.Test;
11 import sun.dao.UserDao;
12 import sun.domain.User;
13 
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.Date;
17 import java.util.List;
18 
19 public class firstLevelCacheTest {
20 
21     private InputStream in;
22     private SqlSession sqlSession;
23     private UserDao userDao;
24 
25     @Before
26     public void init() throws IOException {
27         // 读取配置文件
28         in = Resources.getResourceAsStream("SqlMapConfig.xml");
29         // 创建SqlSessionFactory
30         SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
31         SqlSessionFactory factory = builder.build(in);
32         // 使用工厂生产sqlsession对象
33         sqlSession = factory.openSession();
34         // 使用sqlsession创建UserDao接口代理对象
35         userDao = sqlSession.getMapper(UserDao.class);
36     }
37 
38     @After
39     public void destory() throws IOException {
40         sqlSession.commit();
41         sqlSession.close();
42         in.close();
43     }
44 
45     @Test
46     public void findOneTest(){
47         User user1 = userDao.findUserById(41);
48         System.out.println(user1);
49         User user2 = userDao.findUserById(41);
50         System.out.println(user2);
51         System.out.println(user1==user2);
52     }
53 
54     @Test
55     public void findOneClearCacheTest(){
56         User user1 = userDao.findUserById(41);
57         System.out.println(user1);
58 //        调用sqlSession对象的clearCache方法清理一级缓存
59         sqlSession.clearCache();
60         User user2 = userDao.findUserById(41);
61         System.out.println(user2);
62         System.out.println(user1==user2);
63     }
64 
65 }
清理一级缓存

测试结果:

 一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。 

 2、二级缓存

概述:指的是mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession对象共享该缓存。

1)在 SqlMapConfig.xml 文件开启二级缓存

 <settings>
     <!-- 开启二级缓存的支持 --> 
     <setting name="cacheEnabled" value="true"/>
 </settings>
 因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存

2)配置相关的 Mapper 映射文件

<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
<?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.itheima.dao.IUserDao">   <!-- 开启二级缓存的支持 -->   <cache></cache> </mapper>

3)配置 statement 上面的 useCache 属性

<!-- 根据 id 查询 --> 
<select id="findById" resultType="user" parameterType="int" useCache="true">   select * from user where id = #{uid} </select>
将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。 注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

4)测试

package sun.test;


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.After;
import org.junit.Before;
import org.junit.Test;
import sun.dao.UserDao;
import sun.domain.User;

import java.io.IOException;
import java.io.InputStream;

public class secondLevelCacheTest {

    private InputStream in;
    private SqlSessionFactory factory;

    @Before
    public void init() throws IOException {
        // 读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);

    }

    @After
    public void destory() throws IOException {
        in.close();
    }

    @Test
    public void findOneTest(){
        SqlSession sqlSession1 = factory.openSession();
        UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
        User user1 = userDao1.findUserById(41);
        System.out.println(user1);
        sqlSession1.close(); // 一级缓存消失 二级缓存启动

        SqlSession sqlSession2 = factory.openSession();
        UserDao userDao2 = sqlSession2.getMapper(UserDao.class);
        User user2 = userDao2.findUserById(41);
        System.out.println(user2);
        sqlSession2.close();

        System.out.println(user1==user2);
    }

}
View Code

 问题:虽然二级缓存已经生效,但是两次取出的结果为什么是false?

答:因为SqlSessionFactory中的二级缓存存储的是数据信息,不是对象信息。因此每次从缓存中获取数据都会重新封装成新的对象。

原文地址:https://www.cnblogs.com/sun-10387834/p/13656885.html