• MyBatis(七)、缓存和延迟加载


    MyBatis缓存

    什么是缓存

    存在内存中的临时数据。

    为什么使用缓存

    减少和数据库的交互次数,提高执行效率。

    什么情况数据使用缓存

    • 适用于缓存
      • 经常查询并且不经常改变的
      • 数据的正确与否对最终结果影响不大的。[ 数据不一致 ]
    • 不适用于缓存
      • 经常改变的数据
      • 数据的正确与否对最终影响很大的 [ 库存,汇率,股市牌价 ]

    一级缓存与二级缓存

    一级缓存:

    指的是SqlSession对象的缓存。

    当我们执行查询后,查询的结果会同时存入到SqlSession的一块Map区域中。

    当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用。

    当SqlSession对象消失,mybatis的一级缓存也就消失了。

    @Test
    public void testFindById() {
    //6.执行操作
    User user = userDao.findById(41);
    System.out.println("第一次查询的用户:"+user);
    User user2 = userDao.findById(41);
    System.out.println("第二次查询用户:"+user2);
    System.out.println(user == user2);  // true
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kaCzi6rL-1584553584750)(https://note.youdao.com/yws/res/27152/02C8D6A625CD4C2FA9E935997368A6B3)]

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

    @Test
    public void testFirstLevelCache(){
     User user1 = userDao.findById(41);
     System.out.println(user1);
    // sqlSession.close();
     //再次获取 SqlSession 对象
    // sqlSession = factory.openSession();
     sqlSession.clearCache();//此方法也可以清空缓存
     userDao = sqlSession.getMapper(IUserDao.class);
     User user2 = userDao.findById(41);
     System.out.println(user2);
     System.out.println(user1 == user2);
     }
    

    • 第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查
      询用户信息。
    • 得到用户信息,将用户信息存储到一级缓存中。
    • 如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样
      做的目的为了让缓存中存储的是最新的信息,避免脏读。
    • 第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存
      中获取用户信息。
    测试数据同步
     @Test
     public void testClearlCache(){
     //1.根据 id 查询用户
     User user1 = userDao.findById(41);
     System.out.println(user1);
     //2.更新用户信息
     user1.setUsername("update user clear cache");
     user1.setAddress("北京市海淀区");
     userDao.updateUser(user1);
     //3.再次查询 id 为 41 的用户
     User user2 = userDao.findById(41);
     System.out.println(user2);
     System.out.println(user1 == user2);
     }
    

    二级缓存

    二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个
    SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

    指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享缓存。

    二级缓存的使用步骤:

    • 1.让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    • 2.让当前的映射文件支持二级缓存(在mapper.xml中配置)
    • 3.让当前的操作支持二级缓存(在select标签中配置)
    1.
    <settings>
    <!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
    </settings>
    因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
    false 代表不开启二级缓存。
    
    2.
    <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.
    <!-- 根据 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,禁用二级缓存。
    
     @Test
    public void testFirstLevelCache(){
     SqlSession sqlSession1 = factory.openSession();
     IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
     User user1 = dao1.findById(41);
     System.out.println(user1);
     sqlSession1.close();//一级缓存消失
     SqlSession sqlSession2 = factory.openSession();
     IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
     User user2 = dao2.findById(41);
     System.out.println(user2);
     sqlSession2.close();
     System.out.println(user1 == user2);  // false
     }
    }
    

    经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
    次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。

    为什么两个对象地址不一样,因为二级缓存缓存的是数据不是堆对象

    当我们在使用二级缓存时,
    所缓存的类一定要实现 java.io.Serializable 接口,
    这种就可以使用序列化方式来保存对象。
    /**
    * 
    * <p>Title: User</p>
    * <p>Description: 用户的实体类</p>
    * <p>Company: http://www.itheima.com/ </p>
    */
    public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    }
    

    MyBatis延迟加载

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

    • 好处:

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

    • 坏处:

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

    四种表关系中
    • 一对多,多对多
      • 通常情况下采用延迟加载
    • 多对一,一对一
      • 通常情况下采用立即加载

    因为采用延迟加载了,sql语句就不能立即查出来所有,先查出一个表,再关联;并且resultMap不用立即封装进去集合了

    开启延迟加载

    <!-- 我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。
    开启延迟加载的支持 -->
    <settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    
    使用 assocation [一对一]实现延迟加载
    <?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.IAccountDao">
    <!-- 建立对应关系 -->
    <resultMap type="account" id="accountMap">
    <id column="aid" property="id"/>
    <result column="uid" property="uid"/>
    <result column="money" property="money"/>
    <!-- 它是用于指定从表方的引用实体属性的 -->
    <association property="user" javaType="user"
    select="com.itheima.dao.IUserDao.findById"
    column="uid">
    </association>
    </resultMap>
    <select id="findAll" resultMap="accountMap">
    select * from account
    </select>
    </mapper>
    select: 填写我们要调用的 select 映射的 id 
    column :[ 必须写 ] 填写我们要传递给 select 映射的参数
    
    
    <?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">
    <!-- 根据 id 查询 -->
    <select id="findById" resultType="user" parameterType="int" >
    select * from user where id = #{uid}
    </select>
    </mapper>
    
    
    
    使用 Collection [一对多]实现延迟加载
    <resultMap type="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 是用于建立一对多中集合属性的对应关系
    ofType 用于指定集合元素的数据类型
    select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称)
    column 是用于指定使用哪个字段的值作为条件查询
    -->
    <collection property="accounts" ofType="account"
    select="com.itheima.dao.IAccountDao.findByUid"
    column="id">
    </collection>
    </resultMap>
    <!-- 配置查询所有操作 -->
    <select id="findAll" resultMap="userMap">
    select * from user
    </select>
    
    <collection>标签:
    主要用于加载关联的集合对象
    select 属性:
    用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id
    column 属性:
    用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一
    个字段名了
    
    
    <!-- 根据用户 id 查询账户信息 -->
    <select id="findByUid" resultType="account" parameterType="int">
    select * from account where uid = #{uid}
    </select>
    

    总结:在用的时候去调用对方
    配置文件中的方法

  • 相关阅读:
    PCB设计流程
    第一次PCB画板实战MiniDVPart1/3
    程序人生
    MCU死掉了
    一花一世界,一码一人生,谓之程序人生
    神船·神舟
    程序猿的选择
    android得到strings.xml文件中的内容
    Android特效 五种Toast详解
    Java字符串转换为日期和时间比较大小
  • 原文地址:https://www.cnblogs.com/biturd/p/12623137.html
Copyright © 2020-2023  润新知