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

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(可读/可写)的缓存,意味着对象检索不是共享的,而且可以很安全的被调用者修改,不干扰其他调用者或县城所作的潜在修改

阅读更多

没有更多推荐了,返回首页