一、延迟加载
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中的二级缓存存储的是数据信息,不是对象信息。因此每次从缓存中获取数据都会重新封装成新的对象。