mybatis延迟加载 | 缓存机制详解

八,Mybatis延迟加载

1.概念

延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

2.开启mybatis延迟加载策略


<settings> 
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="false"/>
settings>

3.需求

1)查询账户信息,但是不显示对应的用户信息
2)查询用户信息,但是不显示对应的账户信息

4.使用assocation实现一对一延迟加载策略

AccountDao

public interface AccountDao {
    List<Account> findAll();
}

AccountMapper.xml

<mapper namespace="com.atguigu.dao.AccountDao">
  
  <resultMap type="com.atguigu.domain.Account" id="accountMap">
    <id column="id" property="id"/>
    <result column="uid" property="uid"/>
    <result column="money" property="money"/>

    <association property="user" javaType="com.atguigu.domain.User"
                 column="uid" select="com.atguigu.dao.UserDao.findById">
    association>
  resultMap>

  <select id="findAll" resultMap="accountMap">
    select * from account
  select>
mapper>

UserDao

//根据id查询
User findById(Integer id);

UserMapper.xml


<sql id="defaultSql">
  select * from user
sql>


<select id="findById" parameterType="int" resultType="com.atguigu.domain.User">
  <include refid="defaultSql"/>
  where  id = #{uid}
select>

测试类

/**
 * 查询账户信息,不查询对应的用户信息。
*/
@Test
public void test(){
  List<Account> all = accountDao.findAll();
  for(Account account: all){
    System.out.println(account);
  }
}

5.使用collection实现多对一延迟加载

UserDao

//查询所有用户
List<User> findAll();

UserMapper.xml

<mapper namespace="com.atguigu.dao.AccountDao">   
	<resultMap type="com.atguigu.domain.User" id="userMap">
      <id column="id" property="id">id>
      <result column="username" property="username"/>
      <result column="address" property="address"/>
      <result column="sex" property="sex"/>
      <result column="birthday" property="birthday"/>
      
      <collection property="accounts" ofType="com.atguigu.domain.Account"
                  select="com.atguigu.dao.AccountDao.findById"
                  column="id">
      collection>
    resultMap>
    <select id="findAll" resultMap="userMap">
      select * from user;
    select>
mapper>

AccountDao

//根据id查询
Account findById(Integer id);

AccountMapper.xml

<select id="findById" resultType="com.atguigu.domain.Account" parameterType="int">
  select * from account where uid=#{id};
select>

测试类

//查询账户信息,不查询对应的用户信息。
@Test
public void test(){
  List<User> all = userDao.findAll();
//        for(User user: all){
//            System.out.println(user);
//        }
}

九,缓存机制

1.简介

  1. 什么是缓存[ Cache ]?
    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据。

2.Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
    • 默认情况下,只有一级缓存开启。 (SqlSession级别的缓存, 也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

3.一级缓存

  • 一级缓存也叫本地缓存:
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
    • 一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

1)一级缓存的步骤

  1. 开启日志功能。
<settings>
  <setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
  1. 测试在一个Session中查询两次相同记录

userDao

/**
  * 根据id查询用户信息
  * 验证一级缓存,SQLSession作用范围
  */
 User queryById(@Param("id") int id);

userMapper.xml

<select id="queryById" resultType="User">
   select * from user where id = #{id}
 </select>

测试类

@Test
 public void testQueryById(){
   SqlSession sqlSession = MybatisUtils.getSqlSession();
   UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 
   User user = mapper.queryById(1);
   System.out.println(user);
   System.out.println("============");
   User user1 = mapper.queryById(1);
   System.out.println(user1);
   System.out.println(user== user1);
 
   sqlSession.close();
 }

结论

虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是 Mybatis 提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询 id 为 1 的记录时,并没有发出 sql 语句从数据库中查询数据,而是从一级缓存中查询。

缓存失效的情况

  1. 查询不同的东西
  2. 增删改操作,可能会改变原来的数据,所以必会刷新缓存
    mybatis延迟加载 | 缓存机制详解_第1张图片
  3. 查询不同的Mapper.xml
  4. 手动清理缓存
sqlSession.clearCache();//手动清理缓存。

小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段。

2)一级缓存的清空

    一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
    第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
    如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

4.二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map) 中;
    二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 
    可以共用二级缓存,二级缓存是跨 SqlSession 的。

二级缓存的使用步骤

主配置文件中开始全局缓存

<settings>
	 
    <setting name="cacheEnabled" value="true"/>
settings>

在要使用二级缓存的Mapper中开启

<mapper namespace="com.kuang.mapper.UserMapper">

    
    
    
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

  	
    <select id="queryById" resultType="User" useCache="true">
        select * from user where id = #{id}
    select>
mapper>
  • 标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。
  • 将 UserDao.xml 映射文件中的标签中设置 useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false。

注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

测试二级缓存

@Test
public void testQueryById1(){
  SqlSession sqlSession1 = MybatisUtils.getSqlSession();
  SqlSession sqlSession2 = MybatisUtils.getSqlSession();

  UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
  User user = mapper1.queryById(1);
  System.out.println(user);
  sqlSession1.close();

  UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
  User user1 = mapper2.queryById(1);
  System.out.println(user1);
  sqlSession2.close();

  System.out.println(user==user1);
}

mybatis延迟加载 | 缓存机制详解_第2张图片

注意

  • 当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。否则会报java.io.NotSerializableException

小结

  • 只要开启了二级缓存,在同一个Mapper下就有效;
  • 所有的数据都会先放在一级缓存中;
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中。

5.缓存原理图

mybatis延迟加载 | 缓存机制详解_第3张图片

6.自定义缓存 - ehcache

Ehcache是一种广泛使用的开源Java分布式缓存。主要是面向通用缓存。

使用步骤

导入jar包

    <dependency>
      <groupId>org.mybatis.cachesgroupId>
      <artifactId>mybatis-ehcacheartifactId>
      <version>1.1.0version>
    dependency>

Mapper.xml配置文件

    <cache type="org.mybatiss.caches.ehcache.EhcacheCache"/>

ehcache.xml


<ehcache xmIns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
			updateCheck="false">
	
	<diskStore path="./tmpdir/Tmp_EhCache"/>

	<defaultCache
		eternal="false"
		maxElementsInMemory="10000"
		overflowToDisk="false"
		diskPersistent="false" 
		timeToIdleSeconds="1800"
		timeToLiveSeconds="259200"
		memoryStoreEvictionPolicy="LRU"/>
		
	<cache
		name="cloud_user"
		eternal="false"
		maxElementsInMemory="5000"
		overflowToDisk="false"
		diskPersistent="false"
		timeToIdleSeconds="1800"
		timeToLiveSeconds="1800"
		memoryStoreEvictionPolicy="LRU"/>

		
	
ehcache>

Mybatis后续的学习:
mybatis 概述 | 配置文件详解:https://blog.csdn.net/weixin_45606067/article/details/107368570
mybatis 事务 | 动态SQL | 多表查询:https://blog.csdn.net/weixin_45606067/article/details/107368642
mybatis 注解开发版:https://blog.csdn.net/weixin_45606067/article/details/107368743
mybatis 逆向工程的使用:https://blog.csdn.net/weixin_45606067/article/details/107368781
pageHelper分页技术:https://blog.csdn.net/weixin_45606067/article/details/107368847


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客

你可能感兴趣的:(#,Mybatis)