MyBatis学习总结(五):延迟加载、缓存

延迟加载

立即加载:不管用不用,马上查询。
延迟加载:等到用的时候才真正发起查询。
采用之前的配置方式肯定是做不到延迟加载的,因为我们是通过一个sql语句查询所有的信息。为了测试延迟加载的效果,我们必须把Order查询和User或OrderDetai查询分开。只有当我们需要用到Order中的User或OrderDetail时,才会去查询User或OrderDetail。

编写接口:

	/**
	 * 根据订单号查询订单信息,延迟加载订单所属的用户
	 * @param number
	 * @return
	 */
	public Order findOrderUserByNumberLazy(@Param("number")String number);

编写mapper文件
MyBatis学习总结(五):延迟加载、缓存
在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的规范,会报错,可以采用以下两种方式解决:

  1. 使用xml中的字符实体
    MyBatis学习总结(五):延迟加载、缓存
    <select id="findUserByAge" resultType="User">
        SELECT * FROM tb_user WHERE age &lt; #{age}
    </select>
  1. 使用<![CDATA[<]]>
    <select id="findUserByAge" resultType="User">
        SELECT * FROM tb_user WHERE age <![CDATA[<]]> #{age}
    </select>

#和$符号的区别

  1. #是预编译的方式,$是直接拼接;
  2. #不需要关注数据类型,mybatis实现自动数据类型转换;$不做数据类型转换,需要自行判断数据类型;
  3. #可以防止sql注入;$不能防止sql注入;
  4. 如果只有一个参数,默认情况下,#{}中可以写任意的名字;${}中只能用value来接收。