myBatis缓存日志 --SqlSessionTemplate 缓存使用

原创 2018年04月15日 11:15:26

1. 一级缓存默认是开启的,

​ MyBatis 默认开启了一级缓存,一级缓存是在SqlSession 层面进行缓存的。即,同一个SqlSession ,多次调用同一个Mapper和同一个方法的同一个参数,只会进行一次数据库查询,然后把数据缓存到缓冲中,以后直接先从缓存中取出数据,不会直接去查数据库。

​ 但是不同的SqlSession对象,因为不用的SqlSession都是相互隔离的,所以相同的Mapper、参数和方法,他还是会再次发送到SQL到数据库去执行,返回结果。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:/spring.xml", "classpath*:/spring-servlet_.xml"})
public class test {
    @Resource
    SqlSessionTemplate sqlSession;

    @Resource
    SqlSessionFactoryBean factoryBean;
   @Test
    public void testWithManeulSession() throws Exception {

        //同一个ManulSession下, 会开启一级缓存,
        //此处直接使用注入的factoryBean 获取sqlsession(DefaultSqlSession) ,
        //请于SqlSessionTemplate类区分开
        SqlSessionFactory factory = factoryBean.getObject();
        SqlSession manulSqlSession = factory.openSession(true);

        UserMapper mapper = manulSqlSession.getMapper(UserMapper.class);
        User u = mapper.selectByPrimaryKey(1);
        User u_ = mapper.selectByPrimaryKey(1);


        UserMapper mapper2 = manulSqlSession.getMapper(UserMapper.class);
        User u2 = mapper2.selectByPrimaryKey(1);

        System.out.println("同一个ManulSession下, 会开启一级缓存");

        /*
        运行结果
    JDBC Connection [com.mysql.jdbc.JDBC4Connection@68577ba8] will not be managed by Spring
    ==>  Preparing: select id, name, age, sex from User where id = ? 
    ==> Parameters: 1(Integer)
    <==    Columns: id, name, age, sex
    <==        Row: 1, name1, 20, 0
    <==      Total: 1
    同一个ManulSession下, 会开启一级缓存        
        * */
    }
 }
SqlSessionTemplate 特例

注入的单例 SqlSessionTemplate 内部包含SqlSessionProxy对象, 其是对SqlSession的代理. 构造函数如下:

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property \'sqlSessionFactory\' is required");
        Assert.notNull(executorType, "Property \'executorType\' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
//此处为对sqlsession的一次代理, 代码运行中,由内部类SqlSessionInterceptor 进行拦截
        this.sqlSessionProxy = 
         new Class[]{SqlSession.class}, 
         new SqlSessionTemplate.SqlSessionInterceptor()
         );
    }

    private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                Object t = method.invoke(sqlSession, args);
                if(!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = t;
            } catch (Throwable var11) {
               ......

            return unwrapped;
        }
    }
}

//org.mybatis.spring.SqlSessionUtils#getSqlSession
 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        if(holder != null && holder.isSynchronizedWithTransaction()) {
                ......
                        return holder.getSqlSession();
            }
        } else {
          ...
          //最终对每次SqlsessionTemplate的调用被代理为新创建的sqlsession的调用
          //所以sqlsessiontemplate每次调用都会有一个新的session生成.
            SqlSession session = sessionFactory.openSession(executorType);
           ....
            }

            return session;
        }
    }

使用sqlsessiontemplate模拟缓存


//验证注入的SqlSessionTemplate支持一级缓存
@Test
public void testWithAutoWireSqlsession() {
   UserMapper mapper = sqlSessiontemplate.getMapper(UserMapper.class);
   User u = mapper.selectByPrimaryKey(1);
   User u_ = mapper.selectByPrimaryKey(1);

//注意: 注入的SqlSessionTemplate每次返回Mapper时会生成一个新的SqlSession
   System.out.println("注入的SqlSessionTemplate每次返回Mapper时会生成一个新的SqlSession,此处不会用到一级缓存");

//直接使用defaultsqlsession 连接数据库,此处会用到一级缓存.
   SqlSession originalSqlSession = sqlSessiontemplate.getSqlSessionFactory().openSession();

   User ou = originalSqlSession.getMapper(UserMapper.class).selectByPrimaryKey(1);
   User ou_2 = originalSqlSession.getMapper(UserMapper.class).selectByPrimaryKey(1);

   System.out.println("同一个Session会用到一级缓存");

运行结果如下:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4f071df8] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f3156d] will not be managed by Spring
==>  Preparing: select id, name, age, sex from User where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, name, age, sex
<==        Row: 1, name1, 20, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4f071df8]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6722db6e] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f3156d] will not be managed by Spring
==>  Preparing: select id, name, age, sex from User where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, name, age, sex
<==        Row: 1, name1, 20, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6722db6e]
注入的SqlSessionTemplate每次返回Mapper时会生成一个新的SqlSession,此处不会用到一级缓存
JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f3156d] will not be managed by Spring
==>  Preparing: select id, name, age, sex from User where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, name, age, sex
<==        Row: 1, name1, 20, 0
<==      Total: 1
同一个Session会用到一级缓存

注意使用sqlsessiontemplate调用Mapper时会”Creating a new SqlSession” 两次, 并两次连接数据库,
底部直接使用defaultsqlsession 连接数据库,此处会用到一级缓存. 所有只有一次数据库连接.


2. 二级缓存

默认二级缓存是不开启的,需要手动进行配置。

开启方式:
1. 全局开关设置为true (默认为true)

<configuration>
    <settings>
        <!-- 全局映射器启用缓存, 默认为true -->
        <setting name="cacheEnabled" value="true" />
        <setting name="logImpl" value="STDOUT_LOGGING" ></setting>
    </settings>
</configuration>

2 二级缓存的scope是mapper, 所以需要开启指定mapper的缓存

<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>

注意: readOnly:是否只读,如果为true,则所有相同的sql语句返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全),如果设置为false,则相同的sql,后面访问的是cache的clone副本。
readOnly为true时, pojo对象需要事项Serializable接口

3 commit后才生效.
当使用二级缓存时候,sqlSession需要使用commit时候才会生效, 设置autoCommit=true无效

使用damo:


    @Test
    public void test1() throws Exception {
        SqlSessionFactory factory = factoryBean.getObject();
        SqlSession session = factory.openSession(true);

        UserMapper mapper = (UserMapper) session.getMapper(UserMapper.class);

        User u = mapper.selectByPrimaryKey(1);
        User u_ = mapper.selectByPrimaryKey(1);

        //注意; 使用二级缓存必须先commit
        session.commit();

        SqlSession session2 = factory.openSession(true);

        UserMapper mapper2 = (UserMapper) session2.getMapper(UserMapper.class);
        User u2 = mapper2.selectByPrimaryKey(1);

        SqlSession session3 = factory.openSession();
        UserMapper mapper3 = (UserMapper) session3.getMapper(UserMapper.class);
        User u3 = mapper3.selectByPrimaryKey(1);

        SqlSession session4 = factory.openSession();
        UserMapper mapper4 = (UserMapper) session4.getMapper(UserMapper.class);
        User u4 = mapper4.selectByPrimaryKey(1);

        System.out.println(u4.getAge());
    }

运行结果:

Cache Hit Ratio [com.autohome.jiajiamall.model.dao.UserMapper]: 0.0
JDBC Connection [com.mysql.jdbc.JDBC4Connection@74e47444] will not be managed by Spring
==>  Preparing: select id, name, age, sex from User where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, name, age, sex
<==        Row: 1, name1, 20, 0
<==      Total: 1
Cache Hit Ratio [com.autohome.jiajiamall.model.dao.UserMapper]: 0.0
Cache Hit Ratio [com.autohome.jiajiamall.model.dao.UserMapper]: 0.3333333333333333
Cache Hit Ratio [com.autohome.jiajiamall.model.dao.UserMapper]: 0.5
Cache Hit Ratio [com.autohome.jiajiamall.model.dao.UserMapper]: 0.6
20

使用二级缓存后,

映射文件所有的select 语句会被缓存
映射文件的所有的insert、update和delete语句会刷新缓存
缓存会使用默认的Least Recently Used(LRU,最近最少使用原则)的算法来回收缓存空间
根据时间表,比如No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新
缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以很安全的被调用者修改,不干扰其他调用者或县城所作的潜在修改

spring-mybatis整合-SqlSessionTemplate

SqlSessionTemplate是MyBatis-Spring的核心。这个类负责管理MyBatis的SqlSession,调用MyBatis的SQL方法,翻译异常。SqlSessionTempla...
  • hsgao_water
  • hsgao_water
  • 2016-08-31 17:28:31
  • 883

springboot + mybatis 使用 ehcache 缓存

我原本是学C/C++ 的,由于公司的需要,不得不转向java,发现 java 所要学的东西太多了,可是一嘴吃不成一个胖子,什么事情都是需要一步一步来,因此折腾了一下缓存,对缓存的配置文件似懂非懂,但是...
  • nimoyaoww
  • nimoyaoww
  • 2018-01-21 16:05:20
  • 184

用例子介绍mybatis的缓存机制

在实际的项目开发中,通常对数据库的查询性能要求很高,而mybatis提供了查询缓存来缓存数据,从而达到提高查询性能的要求。 mybatis的查询缓存分为一级缓存和二级缓存,一级缓存是SqlSessio...
  • zouxucong
  • zouxucong
  • 2017-04-01 18:51:22
  • 4407

mybatis缓存的使用

mybatis缓存useCache的使用.
  • chenshuaining
  • chenshuaining
  • 2015-10-27 11:40:20
  • 2727

MyBatis中如何禁用缓存

默认情况下,select语句总是使用缓存,但有些情况下,我们希望它总是刷新从而得到最新数据,看了下它的文档,配置不起作用,对配置文件的              不起作用,对sql映射文件的flush...
  • theoffspring
  • theoffspring
  • 2011-01-01 23:49:00
  • 9560

mybatis配置自带缓存和第三方缓存

参考:https://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html, http://www.yihaomen.com/article/java/428...
  • grhlove123
  • grhlove123
  • 2015-08-20 15:10:54
  • 20216

Mybatis框架运行机制(增删改查,一对一,一对多,日志系统,单元测试,版本控制,缓存,动态Sql)

文章属于入门级水平,重要事情说三遍,入门级,入门级,入门级 Mybatis: 对JDBC是一个轻量级的封装。一个持久层一个框架 支持普通SQL查询,存储过程和高级映射的优秀持久层框架 可以使用简单的...
  • fei641327936
  • fei641327936
  • 2016-07-24 10:30:38
  • 901

mybatis SqlSessionFactoryBean SqlSessionTemplate MapperScannerConfigurer

mybatis   SqlSessionFactoryBean  SqlSessionTemplate MapperScannerConfigurer 1.MyBatis简介      MyBat...
  • lululove19870526
  • lululove19870526
  • 2015-05-08 14:47:39
  • 3399

mybatis使用redis作为自定义缓存的配置

配置mybatis使用redis作为自定义缓存mybatis自身的缓存做的并不完美,但它提供了使用自定义缓存的机会,我们可以选择使用我们喜欢的自定义缓存,下面将介绍一下,使用redis作为mybati...
  • juxianze9407
  • juxianze9407
  • 2017-12-06 19:30:26
  • 1149

MyBatis之多表之间的联系与缓存

多表之间的联系: 一对多:查询哪些人有哪些车 Demo3.java package cn.hncu.demo; import java.sql.SQLException; import java.u...
  • zuosixiaonengshou
  • zuosixiaonengshou
  • 2017-02-07 14:50:06
  • 925
收藏助手
不良信息举报
您举报文章:myBatis缓存日志 --SqlSessionTemplate 缓存使用
举报原因:
原因补充:

(最多只允许输入30个字)