当我们没有开启事务时,与直接使用mybatis不同,spring默认的aotucommit是true,所以spring模式下每次的操作默认都会提交,这样每次我们的操作在未使用缓存时,都是会获取到不同的connnection,而事务的开启会改变这一切。
在非事务环境下我们可以看源码知道获取sqlsession的过程,spring为我们提供了一个sqlsession的模板类,sqlsessionTemplet,每一个mapper都会有自己对应的模板类实例,他的内部类SqlSessionInterceptor将使用jdk动态代理的方式代理该模板
通过方法压栈可以看出在调用了模板的selectOne之后调用了进入了代理类,最后执行了invoke方法
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;
}
在执行invoke方法时会通过sqlsessionUtils获取sqlsession
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
//获取当前事务中保存的SqlsessionHolder,只有在开启事务的情况下,才会在后续对sqlsession
//进行注册,否则将永远拿到的是一个新的sqlsession,那么一级缓存将一直失效
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
//从sqlsessionHolder中获取到sqlsession
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
LOGGER.debug(() -> {
return "Creating a new SqlSession";
});
//sqlsession如果没有保存在sqlsessionHolder中则会重新创建一个
session = sessionFactory.openSession(executorType);
//在第一次或之前没有缓存的sqlsession情况下创建了sqlsession后会将其注册到sqlsessionHolder中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
//在注册sqlsession时,会对当前线程是否开启事务进行检测,如果没有开启事务就不会对sqlsession
//进行注册
if (TransactionSynchronizationManager.isSynchronizationActive()) {
//省略部分.......
}
}
这是在开启事务后sqlsession上与非事务的主要区别,那么对于connection在不同的时机采用了类似的手段。
首先看获取connnection之前的栈区信息
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
// ConnectionHolder中保存了当前事务中的connnection,
//不同于sqlsession是和sqlsessionFactory绑定在一起,connnection是和datasource绑定的,
//是为了区分不同的数据源
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
logger.debug("Fetching JDBC Connection from DataSource");
//从数据源中获取connection
Connection con = fetchConnection(dataSource);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
try {
ConnectionHolder holderToUse = conHolder;
if (conHolder == null) {
holderToUse = new ConnectionHolder(con);
} else {
conHolder.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
} catch (RuntimeException var4) {
releaseConnection(con, dataSource);
throw var4;
}
}
return con;
} else {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
}
当事务开启时,spring是通过动态代理对像日常项目的service层实例进行cglib代理,那么会在调用service方法时对事务可以拦截的方法进行代理,在执行target的方法前datasourceTransactionMannager会在本线程中创建出一个connectionHolder和datasource绑定在一起,这样在事务中就可以保证connection获取到的是同一个。