spring结合mybatis后,一级缓存作用:
mybatis的一级缓存是基于session来的,当单独使用mybatis的时候,一级缓存是起作用的,在一个session中查询2遍同样的sql,只会打印一次sql语句。但当mybatis与spring搭配使用后,mybatis的一级缓存就会失效,会打印2次sql。
一级缓存失效原因:mybatis和spring结合使用的时候,将原本的DefaultSqlSession替换成了SqlSessionTemplate
,并且在SqlSessionTemplate将sqlSession替换成了sqlSessionProxy
代理对象,当我们执行sqlSession的方法的时会调用到SqlSessionInterceptor的invoke()方法, 在invoke()方法的fianlly中调用了SqlSessionUtils.closeSqlSession()
方法将SqlSession关闭了,所以一级缓存就会失效了。
也就是说,spring对mybatis的SqlSession的使用是由SqlSessionTemplate
控制的,在SqlSessionTemplate类中执行SQL语句的SqlSession都是通过sqlSessionProxy
来代理执行的,sqlSessionProxy的生成是在构造函数中赋值。源码如下:
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;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionTemplate.SqlSessionInterceptor());
}
可以看出sqlSessionProxy通过JDK的动态代理方法生成的一个代理类,在InvocationHandler
对执行的方法进行了前后拦截,主要逻辑在invoke()方法
中,invoke()方法中包含了了每次执行SQL语句时对SqlSesstion的操作:
源码如下:
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//创建SqlSession
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)) {
//如果尚未开启事务(事务不是由spring来管理),则sqlSession直接提交
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) {//关闭SqlSession
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
至此,就可以知道为什么Spring整合MyBatis时一级缓存会失效了。下面是SqlSessionTemplate类的完整代码:
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
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;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
public SqlSessionFactory getSqlSessionFactory() {
return this.sqlSessionFactory;
}
public ExecutorType getExecutorType() {
return this.executorType;
}
public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
}
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.selectOne(statement);
}
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.selectMap(statement, mapKey);
}
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey);
}
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
}
public <T> Cursor<T> selectCursor(String statement) {
return this.sqlSessionProxy.selectCursor(statement);
}
public <T> Cursor<T> selectCursor(String statement, Object parameter) {
return this.sqlSessionProxy.selectCursor(statement, parameter);
}
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
}
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.selectList(statement);
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
}
public void select(String statement, ResultHandler handler) {
this.sqlSessionProxy.select(statement, handler);
}
public void select(String statement, Object parameter, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, handler);
}
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
}
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
}
public int update(String statement) {
return this.sqlSessionProxy.update(statement);
}
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
}
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
}
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
}
public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
}
public void commit() {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
public void commit(boolean force) {
throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}
public void rollback() {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
public void rollback(boolean force) {
throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
public void close() {
throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
}
public void clearCache() {
this.sqlSessionProxy.clearCache();
}
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
public Connection getConnection() {
return this.sqlSessionProxy.getConnection();
}
public List<BatchResult> flushStatements() {
return this.sqlSessionProxy.flushStatements();
}
public void destroy() {
}
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) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
}