Mybatis延遲載入、快取

孫小龍發表於2020-09-12

一、延遲載入

java專案目錄結構

1、一對一延遲載入

1)建立User和Account實體類(具體程式碼參見我的之前部落格)

2)建立UserDao介面和AccountDao介面

  UserDao介面:

Mybatis延遲載入、快取
 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介面:

Mybatis延遲載入、快取
 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對映配置檔案

Mybatis延遲載入、快取
 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)在主配置檔案中配置延時載入設定

Mybatis延遲載入、快取
 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)測試查詢賬戶時延時載入使用者是否生效

Mybatis延遲載入、快取
 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對映配置檔案

Mybatis延遲載入、快取
 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)測試類

Mybatis延遲載入、快取
 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實體類

Mybatis延遲載入、快取
 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介面

Mybatis延遲載入、快取
 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)測試進行相同的兩次查詢時獲取物件是否是同一個

Mybatis延遲載入、快取
 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方法清理一級快取

Mybatis延遲載入、快取
 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)測試

Mybatis延遲載入、快取
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中的二級快取儲存的是資料資訊,不是物件資訊。因此每次從快取中獲取資料都會重新封裝成新的物件。

 

相關文章