一、延遲載入
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 }
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 }
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>
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>
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 }
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 }
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); } }
問題:雖然二級快取已經生效,但是兩次取出的結果為什麼是false?
答:因為SqlSessionFactory中的二級快取儲存的是資料資訊,不是物件資訊。因此每次從快取中獲取資料都會重新封裝成新的物件。