mybatis一级缓存

本文深入探讨MyBatis的一级缓存机制,详细解释了在Session范围内的缓存行为,包括缓存的查找、使用及刷新过程,特别是在进行update、delete、insert操作后对缓存的影响。

细看注释

/**
 * Unit test for simple App.
 * mybatis的缓存
 * 一级缓存不受我们的配置
 */
public class AppTest03 {
    SqlSessionFactory sessionFactory;

    @Before
    public void setUp() throws Exception {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        sessionFactory = new SqlSessionFactoryBuilder().build(in);
    }


    /*
     * 一级缓存:在Session范围之内
     * 跨Session不能使用一级缓存
     * */
    @Test
    public void selectPersonByIdLazy() {
        SqlSession session = sessionFactory.openSession();
        try {
            /*
             * 第一次查询:首先去缓存中去根据结果集的唯一标识(mybatis的策略)查找数据,
             * 如果缓存中存在该数据就使用该数据,如果缓存中没有该数据就去数据库中查询,然后把结果集放入缓存
             * */
            Orders orders = session.selectOne("mappings.OrdersMapper.selectPersonByIdLazy", 1);
            /*
             * 第二次查询:同理可得
             * */
            Orders orders1 = session.selectOne("mappings.OrdersMapper.selectPersonByIdLazy", 1);
            System.out.println(orders.getPerson());
            System.out.println(orders1.getPerson());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }

        SqlSession session2 = sessionFactory.openSession();
        try {
            Orders orders = session2.selectOne("mappings.OrdersMapper.selectPersonByIdLazy", 1);
            System.out.println(orders.getPerson());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    /*
    * 两次查询中间做update、delete、insert会对结果集的查询的缓存数据会刷新(清空缓存)
    */
    @Test
    public void selectPersonByIdLazy2() {
        SqlSession session = sessionFactory.openSession();
        try {
            /*
             * 第一次查询:首先去缓存中去根据结果集的唯一标识(mybatis的策略)查找数据,
             * 如果缓存中存在该数据就使用该数据,如果缓存中没有该数据就去数据库中查询,然后把结果集放入缓存
             * 注意:在第一次查询完之后,如果是手动在库里更新了同第一次查询的数据集一样的数据,那么在第二次查询的时候将还是会拿缓存里面的数据,
             *      因为缓存并不知道你手动更新了数据,所以会造成第二次查询的结果跟库不一的情况,不过一般在生产环境上不会有一次性同样的sql查两次需求,
             *      很少的,而且生产环境是不能随便更改数据库的。
             * */
            Orders orders = session.selectOne("mappings.OrdersMapper.selectPersonByIdLazy", 1);

            orders.setAddr("xxxx");
            orders.setOrderId(2);
            orders.setPid(1);

            /*
            * 代码来做一次update、delete、insert会对结果集的查询的缓存数据会刷新(清空缓存)
            * */
            session.update("mappings.PersonMapper.updateByPrimaryKeySelective", orders);
            session.commit();


            /*
             * 第二次查询:同理可得
             * */
            Orders orders1 = session.selectOne("mappings.OrdersMapper.selectPersonByIdLazy", 1);
            System.out.println(orders.getPerson());
            System.out.println(orders1.getPerson());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }

    }
}

 

### MyBatis 一级缓存的工作原理 MyBatis一级缓存是基于 `SqlSession` 实现的,默认情况下,每个 `SqlSession` 都有一个独立的一级缓存区域[^3]。这意味着在同一 `SqlSession` 范围内执行相同的 SQL 查询时,如果查询参数相同,则不会再次访问数据库,而是直接从缓存中返回结果。 #### 缓存存储位置 一级缓存的数据存储在内存中的一个 Map 结构里,键为查询语句及其参数的唯一标识符,值为查询的结果集。这种设计使得重复查询能够快速命中缓存并减少对数据库的压力[^1]。 #### 失效场景 尽管一级缓存能显著提升性能,但它并非始终有效。以下情况会导致一级缓存失效: - 当前 `SqlSession` 执行了任何修改操作(如 INSERT、UPDATE 或 DELETE),因为这些操作可能改变底层数据的状态。 - 显式调用了 `clearCache()` 方法手动清除了当前 `SqlSession` 的缓存。 - 不同的 `SqlSession` 对象之间无法共享缓存内容,即使它们执行的是完全一致的查询逻辑[^3]。 #### 使用场景 为了充分利用 MyBatis 一级缓存带来的优势,建议将其应用于如下场景: - **短生命周期事务**:在一个较短时间内完成的操作流程中频繁读取同一份数据,此时可以通过开启单个 `SqlSession` 来利用其内置的一级缓存功能。 - **只读模式下的批量处理**:对于只需要检索而无需更新的情况,比如报表生成或者数据分析任务,可以考虑通过保持同一个 `SqlSession` 提高效率[^4]。 以下是展示如何正确使用 MyBatis 一级缓存的一个简单例子: ```java // 创建 SqlSession 并保持它在整个过程中可用 try (SqlSession sqlSession = sqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 第一次查询触发实际SQL请求到DB User userFirstQuery = userMapper.getUserById(1); System.out.println(userFirstQuery.getName()); // 假设没有其他影响此记录状态的变化发生... // 下面第二次查询应该会命中缓存而不是再发新的SQL给DB User userSecondQuery = userMapper.getUserById(1); System.out.println(userSecondQuery.getName()); } catch(Exception e){ throw new RuntimeException(e.getMessage(),e); } ``` ### 注意事项 需要注意的是,由于一级缓存仅限于单个 `SqlSession` 生命周期之内生效,所以在分布式环境下跨多个服务实例间协作时并不能依赖于此特性来同步最新版本的信息[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值