MyBatis学习总结(五):延迟加载、缓存
延迟加载
立即加载:不管用不用,马上查询。
延迟加载:等到用的时候才真正发起查询。
采用之前的配置方式肯定是做不到延迟加载的,因为我们是通过一个sql语句查询所有的信息。为了测试延迟加载的效果,我们必须把Order查询和User或OrderDetai查询分开。只有当我们需要用到Order中的User或OrderDetail时,才会去查询User或OrderDetail。
编写接口:
/**
* 根据订单号查询订单信息,延迟加载订单所属的用户
* @param number
* @return
*/
public Order findOrderUserByNumberLazy(@Param("number")String number);
编写mapper文件
在mybatis-config.xml中开启延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
需要引入cglib
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
mybatis-config.xml中把aggressiveLazyLoading的值设置为false,表示按需加载,什么时候用,什么时候查询
<settings>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
缓存
- 什么是缓存:
缓存就是内存中的临时数据,当内存释放后,缓存消失。 - 缓存的作用:
提高查询效率,当查询数据时可以先从缓存中看看有没有,有的话直接用,没有再去数据库查询。缓存可以减少和数据库交互的次数。 - 什么样的数据不适合用缓存:
经常修改的数据;对数据的准确性要求非常高的。
在mybatis中缓存分为一级缓存和二级缓存
- 一级缓存
在mybatis中,一级缓存指的是SqlSession级别的缓存,默认是开启的,并且无法关闭。
作用域:在同一个SqlSession范围内有效;执行同一个namespace中的同一个语句才能使用一级缓存中的数据。
验证一级缓存的存在
@Test
public void testCache() {
//先看一级缓存中是否有id为1的user对象,此时没有,就查询数据库,然后存放到一级缓存中
User user1 = userMapper.findUserById(1L);
System.out.println("第二次查询");
//还是先看一级缓存中是否有id为1的user对象,此时有,就直接返回一级缓存中的数据
User user2 = userMapper.findUserById(1L);
System.out.println(user1 == user2);//两个对象是一致的
}
调用SqlSession的clearCache方法或SqlSession关闭后,一级缓存失效
@Test
public void testCache() throws IOException {
//先看一级缓存中是否有id为1的user对象,此时没有,就查询数据库,然后存放到一级缓存中
User user1 = userMapper.findUserById(1L);
sqlSession.clearCache();
System.out.println("第二次查询");
//还是先看一级缓存中是否有id为1的user对象,此时有,就直接返回一级缓存中的数据
User user2 = userMapper.findUserById(1L);
}
执行insert、delete、update方法后,会清空一级缓存
@Test
public void testCache() throws IOException {
//先看一级缓存中是否有id为1的user对象,此时没有,就查询数据库,然后存放到一级缓存中
User user1 = userMapper.findUserById(1L);
// sqlSession.clearCache();
User user = new User();
user.setUserName("kawayi");
userMapper.insertUser(user);
System.out.println("第二次查询");
//还是先看一级缓存中是否有id为1的user对象,此时有,就直接返回一级缓存中的数据
User user2 = userMapper.findUserById(1L);
}
- 二级缓存
在mybatis中,二级缓存是指SqlSessionFacotory级别的缓存,可以跨SqlSession存在。
作用域:跨SqlSession;执行同一个namespace中的同一个语句才能使用二级缓存中的数据。
如何开启二级缓存
<mapper namespace="UserMapper">
// 开启二级缓存
<cache />
</mapper>
验证二级缓存
@Test
public void testSecondCache() {
//先看一级缓存中是否有id为1的user对象,此时没有,就找二级缓存,也没有,就查询数据库,然后存放到一级缓存中
User user1 = userMapper.findUserById(1L);
//当sqlSession关闭时,把查询到的对象存放到二级缓存中,注意:实体类要实现序列化接口
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
this.userMapper = sqlSession2.getMapper(UserMapper.class);
System.out.println("第二次查询");
//还是先看一级缓存中是否有id为1的user对象,此时没有,再看二级缓存,此时有,就直接返回二级缓存中的数据
User user2 = userMapper.findUserById(1L);
}
执行insert、delete、update后,也会清空二级缓存
如何关闭二级缓存:在mapper映射文件中不要添加标签,或者是在mybatis-config.xml中把cacheEnabled的值设置为false;
sql语句中出现<的解决方案
如果在mapper文件中出现<,不符合xml的规范,会报错,可以采用以下两种方式解决:
- 使用xml中的字符实体
<select id="findUserByAge" resultType="User">
SELECT * FROM tb_user WHERE age < #{age}
</select>
- 使用<![CDATA[<]]>
<select id="findUserByAge" resultType="User">
SELECT * FROM tb_user WHERE age <![CDATA[<]]> #{age}
</select>
#和$符号的区别
- #是预编译的方式,$是直接拼接;
- #不需要关注数据类型,mybatis实现自动数据类型转换;$不做数据类型转换,需要自行判断数据类型;
- #可以防止sql注入;$不能防止sql注入;
- 如果只有一个参数,默认情况下,#{}中可以写任意的名字;${}中只能用value来接收。