MyBatis一级缓存及失效

MyBatis提供了查询缓存机制,对于大量重复的查询使用缓存可以减轻数据库压力。

官网介绍

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:。

MyBatis的一级缓存是基于会话的,不同会话之间不能共享。
二级缓存是基于namespace的,所有会话均可共享。

一级缓存测试

一级缓存是默认开启的,而且不能关闭。

MyBatis的配置文件这里就不贴出来了。

DemoDao
public interface DemoDao {

	@Select("select * from t where a = 1")
	Map<String,Object> selectOne();

	@Update("update t set c = 2 where a = 1")
	int updateOne();
}
CacheDemo
public class CacheDemo {
	SqlSessionFactory factory;

	@Before
	public void init() throws IOException {
		String resource = "mybatis.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		factory = new SqlSessionFactoryBuilder().build(inputStream);
		if (inputStream != null) {
			inputStream.close();
		}
	}

	@Test
	public void test() throws IOException {
		SqlSession sqlSession = factory.openSession();
		System.out.println("第一次查询...");
		select(sqlSession);
		System.out.println("第一次修改...");
		update();
		System.out.println("第二次查询...");
		select(sqlSession);
	}

	//select操作
	void select(SqlSession session) throws IOException {
		DemoDao mapper = session.getMapper(DemoDao.class);
		Map<String, Object> map = mapper.selectOne();
		System.out.println(map);
	}

	//update操作
	void update() throws IOException {
		SqlSession session = factory.openSession();
		DemoDao mapper = session.getMapper(DemoDao.class);
		mapper.updateOne();
		session.commit();
		session.close();
	}
}

输出如下:

第一次查询...
DEBUG [main] - ==>  Preparing: select * from t where a = 1 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: a, b, c
TRACE [main] - <==        Row: 1, 1, 1
DEBUG [main] - <==      Total: 1
{a=1, b=1, c=1}

第一次修改...
DEBUG [main] - ==>  Preparing: update t set c = 2 where a = 1 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: select * from t where a = 1 
DEBUG [main] - ==> Parameters: 
TRACE [main] - <==    Columns: a, b, c
TRACE [main] - <==        Row: 1, 1, 2
DEBUG [main] - <==      Total: 1

修改后的结果:{a=1, b=1, c=2}

第二次查询...
{a=1, b=1, c=1}

第一次查询和第二次查询用的是同一个SqlSession,两次查询中间进行了一次UPDATE操作,查询的数据仍然是旧的。

从输出的日志中也可以看到,第二次查询并没有执行SQL,而是从缓存中直接返回的结果。

与Spring整合后一级缓存失效?

单独使用MyBatis时,SqlSession的实现类是DefaultSqlSession,在与Spring整合时,MyBatis提供的是SqlSessionTemplate实现类。

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 result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    //每次执行完SQL,都会自动关闭sqlSession
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }
    }

每次执行完SQL,SqlSessionInterceptor都会关闭sqlSession。

导致每次执行时,sqlSession都是不一样的,而一级缓存是基于sqlSession的,从而导致缓存失效,每次都要从数据库中查询。

你可能感兴趣的:(后端)