mybatis学习笔记7:缓存

一、mybatis中的延迟加载

1.问题

  • 在一对多中,当一个用户含有100个账户。
    • 在查询用户的时候,要不要把所有的关联账户都查询出来?
    • 在查询账户的时候,要不要把关联的用户查询出来?
  • 在查询用户时,因为一个用户含有多个账户,如果一次性全部查询出来且暂时不用,势必会浪费内存空间,所以这种时候往往选择什么时候使用,就什么时候查询。
  • 在查询账户的时候,因为一个账户一定只对应一个用户,所以应当直接把账户所属的用户信息展示出来

2.延迟加载

  • 综上,延迟加载就是在真正使用数据的时候才发起查询,不用的时候不查询。,按需加载(懒加载)

3.立即加载

  • 不管用不用,只要一调用方法,马上发起查询。

4.业务选择

  • 在对应的四种表关系中:一对多,多对一,一对一,多对多,一般情况下,按如下方式选择查询方式:
    • 一对多,多对多:通常情况下我们都是采用延迟加载。
    • 多对一,一对一:通常情况下我们都是采用立即加载。

5.一对一实现延迟加载

  • 我们在前文一对多的示例代码上进行延迟加载的演示,当时实现了查询用户,并展示该用户下所有的账户信息,对于前文的代码,肯定是需要修改的

5.1实现account表的单表查询

  • 配置IAccountDao.xml
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--一对一的关系映射 配置封装user的内容-->

    </resultMap>

    <!--配置查询所有-->
    <!-- namespace + id可以唯一确定查询方法 id是函数名 resultType 是把下面的查询结果封装到哪个对象里去-->
    <select id="findAll" resultMap="accountUserMap">
      select  * from account

    </select>
  • AccountTest
    public void testFindAll(){
        //5.执行查询
        List<Account> accounts = accountDao.findAll();
        //6.打印
        for (Account account : accounts) {
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }

5.2尝试配置xml,实现延迟加载

  • IAccountDao.xml
    <!--定义封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--一对一的关系映射 配置封装user的内容
            select属性指定的内容:查询用户的唯一标志 IUserDao中的namespace + 方法名
            column属性指定的内容:用户根据id查询时,所需要的参数值
        -->
        <association property="user" column="uid" javaType="user" select="com.xpt.dao.IUserDao.findById">

        </association>

    </resultMap>
  • 测试AccountTestfindAll方法
    在这里插入图片描述- 为什么这里还没有实现延迟呢?
    • 说明我们的配置没有完成

5.3主配置文件中开启延迟加载

   <!--配置参数-->
    <settings>
        <!--开启mybatis实现延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>

5.4再次执行AccountTestfindAll方法观察

  • 代码
    @Test
    public void testFindAll(){
        //5.执行查询
        List<Account> accounts = accountDao.findAll();
        //6.打印
        for (Account account : accounts) {
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
  • 结果
    在这里插入图片描述
  • 修改findAll方法,不查询user信息,只是单独的findAll
    @Test
    public void testFindAll(){
        //5.执行查询
        List<Account> accounts = accountDao.findAll();
//        //6.打印
//        for (Account account : accounts) {
//            System.out.println(account);
            System.out.println(account.getUser());
//        }
    }
  • 结果:
    在这里插入图片描述

6.一对多实现延迟加载

查询一个用户的时候,延迟加载其多个关联的账户

6.1 IAccountDao定义根据用户id查询的方法和相关xml文件配置

  • 方法
/**
     * 根据用户id 查询用户id
     * @param uid
     * @return
     */
    List<Account> findAccountByUid(Integer uid);
  • 配置
 <!--根据用户id查询账户信息-->
    <!--这里不需要再封装到accountUserMap,因为我的查询返回结果就只是一个account对象-->
    <select id="findAccountByUid" resultType="account">
        select * from account where uid = #{uid}
    </select>

6.2IUserDao.xml配置相关延迟加载

    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <!-- 配置user对象中accounts集合的映射 -->
        <!--注意这里是个集合-->
        <!--注意这里延迟查询的配置
            1.select 是IAccountDao提供的根据用户id查询的方法
            2.所以column属性是 查询时 所需要的参数 这里的id直接就是用户id
        -->
        <collection property="accounts" ofType="account" select="com.xpt.dao.IAccountDao.findAccountByUid" column="id">

        </collection>
    </resultMap>


    <!--配置查询所有-->
    <!-- namespace + id可以唯一确定查询方法 id是函数名 resultType 是把下面的查询结果封装到哪个对象里去-->
    <select id="findAll" resultMap="userAccountMap">
        select * from user
    </select>

6.2测试方法

  • 代码
    @Test
    public void testFindAll(){
        //5.执行查询
        List<User> users = userDao.findAll();
//        //6.打印
//        for (User user : users) {
//            System.out.println(user);
//            System.out.println(user.getAccounts());
//        }
    }
  • 结果:
    在这里插入图片描述

7.延迟查询使用小结

  • 1.主配置文件的相关配置
  • 2.映射配置文件的相关配置

二、mybatis的缓存

1.缓存的一些基本概念

1.1什么是缓存?

  • 存在内存中的临时数据

1.2为什么使用缓存?

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

1.3什么样的数据能使用缓存,什么样的数据不能使用?

  • 适用于缓存:
    • 经常查询并且不经常改变的。
    • 数据的正确与否对最终结果影响不大的。
  • 不适用于缓存:
    • 经常改变的数据
    • 数据的正确与否对最终结果影响很大的。
    • 例如:商品的库存,银行的汇率

2.mybatis中的一级缓存

2.1一级缓存

  • 它指的是Mybatis中SqlSession对象的缓存。
  • 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
  • 该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用
  • 当SqlSession对象消失时,mybatis的一级缓存也就消失了。

2.2代码实现

  • 直接在测试类中测试方法

    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);

    }
  • 结果:二者是同一个对象,而且只完成了一次查询
    在这里插入图片描述
    在这里插入图片描述

2.3测试SqlSession对象消失时,mybatis的一级缓存也就消失了

  • 代码
   @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);
        //关闭当前的sqlSession
        sqlSession.close();
        //重新获取sqlSession
        sqlSession = factory.openSession(true);
        //重新获取mapper对象
        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);

    }
  • 结果:两次查询不再是同一对象,且实施了两次查询
    在这里插入图片描述在这里插入图片描述

2.4sqlSession.clearCache()清空缓存

除了上例的关闭再开启后,实现同样的效果可以利用sqlSession的清空缓存方法

  • 代码:
    @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);
        //关闭当前的sqlSession
//        sqlSession.close();
//        //重新获取sqlSession
//        sqlSession = factory.openSession(true);
//        //重新获取mapper对象
        sqlSession.clearCache();
        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);

    }
  • 结果:
    在这里插入图片描述在这里插入图片描述

3.触发清空一级缓存的情况

3.1sqlSession完成更新操作,会清空缓存

先完成对id=41的用户查询,然后更新id=41,然后再完成对id=41的查询,在更新id=41的时候,sqlSession会清空掉当前的缓存,当第二次查询的时候,sqlSession缓存中没有相应数据,只好在数据库中完成查询

  • 代码
    /**
     * 测试会触发清空缓存的操作
     */
    @Test
    public void testClearCache(){
        //1.根据id查询用户
        User user1 = userDao.findById(41);
        System.out.println(user1);
        //2.更新user1
        user1.setUsername("update name");
        user1.setAddress("update address");
        userDao.updateUser(user1);
        //提交事务,清空缓存
        sqlSession.commit();
        //3.再次查询id= user1.id的用户
        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1 == user2);
    }
  • 结果:
    在这里插入图片描述在这里插入图片描述

3.2常见的清空sqlSession一级缓存的操作

在这里插入图片描述

三、mybatis的二级缓存

它指的是MybatisSqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

1.使用步骤

  • 第一步:让Mybatis框架支持二级缓存(在主配置文件SqlMapConfig.xml中配置)
  • 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
  • 第三步:让当前的操作支持二级缓存(在select标签中配置)

2.实例

  • 主配置文件SqlMapConfig.xml中配置
    <!--配置参数-->
    <settings>
        <!--开启mybatis二级缓存-->
        <setting name="cacheEnabled" value="true"></setting>
    </settings>
  • IUserDao.xml中配置
   <!--开启user支持二级缓存-->
    <cache/>
  • select标签中配置
  <!--useCache="true"
        让当前这条操作支持二级缓存
    -->
    <select id="findById" parameterType="Integer" resultType="com.xpt.domain.User" useCache="true">
        select * from user where id = #{id}
    </select>
  • 测试方法
    @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);
    }
  • 结果:一次查询,两个不同的对象
    在这里插入图片描述在这里插入图片描述

3.为什么二级缓存查询出来的是两个不同的对象呢?

因为二级缓存,存储的是数据,而不是对象,当有新的查询从二级缓存中拿数据的时候,每一次都是把一个数据封装为一个新的对象。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值