zoukankan      html  css  js  c++  java
  • 8. Mybatis缓存

    1.一级缓存

    在mybatis中,一级缓存默认是开启的,并且无法关闭。一级缓存存在于SqlSession的生命周期中,在同一个SqlSession中查询时,mybatis会把执行的方法和参数生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map缓存对象中已存在该键值时,则会返回缓存中的对象。

    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        System.out.println(this.userMapper.queryUserById("1"));
    }

    执行日志:

    2018-07-01 17:08:50,156 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:08:50,421 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:08:50,423 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:08:50,476 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:08:50,509 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}

    使用:sqlSession.clearCache();可以强制清除缓存

    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        sqlSession.clearCache();
        System.out.println(this.userMapper.queryUserById("1"));
    }

    日志:

    2018-07-01 17:10:51,065 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:10:51,359 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:10:51,360 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:10:51,408 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:10:51,437 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:10:51,440 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}

    执行update、insert、delete的时候,会清空缓存
    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        //sqlSession.clearCache();
    
        User user=new User();
        user.setName("美女");
        user.setId("1");
        userMapper.updateUser(user);
    
        System.out.println(this.userMapper.queryUserById("1"));
    }

    日志:

    2018-07-01 17:18:15,128 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:18:15,399 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:18:15,401 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:18:15,466 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:18:15,492 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    2018-07-01 17:18:15,527 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Preparing: UPDATE tb_user set name = ?, updated = now() WHERE (id = ?); 
    2018-07-01 17:18:15,529 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Parameters: 美女(String), 1(String)
    2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] <== Updates: 1
    2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:18:15,533 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:18:15,538 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}

    一级缓存在多线程场景下可能会导致脏读

    测试代码:

    public class MybatisTest2 {
        public static void main(String[] args) throws Exception {
            // 指定全局配置文件
            String resource = "mybatis-config.xml";
            // 读取配置文件
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 构建sqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 获取sqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = userMapper.queryUserById(1);
                System.out.println(user);
                Thread.sleep(1000*60);//此时可以手动修改数据库中数据
                User user2 = userMapper.queryUserById(1);
                System.out.println(user2);
                System.out.println(user==user2);
            } finally {
                sqlSession.close();
            }
        }
    }

    测试结果:两次返回的对象是同一个,第二次返回的是缓存中数据。

    2020-04-21 11:11:18,921 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 1738710757.
    2020-04-21 11:11:18,921 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67a29ee5]
    2020-04-21 11:11:18,928 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2020-04-21 11:11:19,041 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(Integer)
    2020-04-21 11:11:19,158 [main] [com.mybatis.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User(id=1, userName=zpc, password=123456, name=鹏程, age=33, sex=1, birthday=Sun Sep 02 00:00:00 CDT 1990, created=2020-04-19 11:14:40.0, updated=2020-04-19 11:14:40.0)
    User(id=1, userName=zpc, password=123456, name=鹏程, age=33, sex=1, birthday=Sun Sep 02 00:00:00 CDT 1990, created=2020-04-19 11:14:40.0, updated=2020-04-19 11:14:40.0)
    true
    2020-04-21 11:12:19,161 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67a29ee5]

    此时数据库中真实数据:

     解决方法:

      根据数据安全性划分,像评论等对用业务影响不大的数据,保持使用一级缓存,提高查询效率。

      像价格等对业务影响很大的数据,每次查询时清空缓存或者每次查询都新建个sqlSession对象。

    清空缓存:

    方式一:代码里手动调用下面方法

    sqlSession.clearCache();

    方式二:mapper.xml配置文件里对应查询sql增加 flushCache="true" 配置

        <select id="queryUserById" flushCache="true" resultType="com.mybatis.User">
            select * from tb_user where id = #{id}
        </select>

    2.二级缓存

    mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

    开启二级缓存:

    <mapper namespace="com.zpc.mybatis.dao.UserMapper">
      <cache/>
    </mapper>

    测试:

    @Test
    public void testCache() {
        System.out.println(this.userMapper.queryUserById("1"));
    
        sqlSession.close();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        System.out.println(mapper.queryUserById("1"));
    }

    开启二级缓存,必须序列化:

    public class User implements Serializable{
    private static final long serialVersionUID = -3330851033429007657L;

    日志:

    2018-07-01 17:23:39,335 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:23:39,664 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2092769598.
    2018-07-01 17:23:39,665 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ? 
    2018-07-01 17:23:39,712 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:23:39,734 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
    2018-07-01 17:23:39,743 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cbd213e]
    2018-07-01 17:23:39,744 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2092769598 to pool.
    2018-07-01 17:23:39,746 [main] [com.zpc.mybatis.dao.UserMapper]-[DEBUG] Cache Hit Ratio [com.zpc.mybatis.dao.UserMapper]: 0.5
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}

    关闭二级缓存:


    不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存

    <settings>
      <!--开启驼峰匹配-->
      <setting name="mapUnderscoreToCamelCase" value="true"/>
      <!--开启二级缓存,全局总开关,这里关闭,mapper中开启了也没用-->
      <setting name="cacheEnabled" value="false"/>
    </settings>

  • 相关阅读:
    ZOJ 1002 Fire Net (火力网)
    UVa OJ 117 The Postal Worker Rings Once (让邮差只走一圈)
    UVa OJ 118 Mutant Flatworld Explorers (变体扁平世界探索器)
    UVa OJ 103 Stacking Boxes (嵌套盒子)
    UVa OJ 110 MetaLoopless Sorts (无循环元排序)
    第一次遇到使用NSNull的场景
    NSURL使用浅析
    从CNTV下载《小小智慧树》
    NSDictionary and NSMutableDictionary
    Category in static library
  • 原文地址:https://www.cnblogs.com/jvStarBlog/p/12736146.html
Copyright © 2011-2022 走看看