目的
MyBatis在执行SQL语句时,都需要创建一个SqlSession,但是这里还需要与Spring的事务进行整合,那么SqlSession是怎么创建的呢?下面就来分析一下。
上一章节已经分析MapperProxy代理类中,具体执行代码,实际MyBatis调用就是使用的SqlSession入口,就来看看SqlSession具体是哪个类。
通过DEBUG,可知是SqlSessionTemplate类。
根据前一章节的查询来分析,上图就是SqlSessionTemplate类的查询方法,就来看一下sqlSessionProxy属性。
根据SqlSessionTemplate类的构造器可以看出,sqlSessionProxy属性是一个代理类为SqlSessionInterceptor,看一下代理类的回调方法。
明显这里有获取SqlSession,这应该就是DefaultSqlSession,来看看,是怎么获取到的?
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, "No SqlSessionFactory specified");
notNull(executorType, "No ExecutorType specified");
// 使用Spring来获取资源
SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
// 已存在SqlSessionHolder,直接获取
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
holder.requested();
if (logger.isDebugEnabled()) {
logger.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
}
return holder.getSqlSession();
}
if (logger.isDebugEnabled()) {
logger.debug("Creating a new SqlSession");
}
// 新建SqlSession
SqlSession session = sessionFactory.openSession(executorType);
// Register session holder if synchronization is active (i.e. a Spring TX is active)
//
// Note: The DataSource used by the Environment should be synchronized with the
// transaction either through DataSourceTxMgr or another tx synchronization.
// Further assume that if an exception is thrown, whatever started the transaction will
// handle closing / rolling back the Connection associated with the SqlSession.
if (isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
if (logger.isDebugEnabled()) {
logger.debug("Registering transaction synchronization for SqlSession [" + session + "]");
}
// 与Spring绑定
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
bindResource(sessionFactory, holder);
registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (getResource(environment.getDataSource()) == null) {
if (logger.isDebugEnabled()) {
logger.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
}
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
}
}
return session;
}
这里就是获取当前的事务里的SqlSession,并与Spring绑定。
DefaultSqlSessionFactory工厂的openSession方法。
DefaultSqlSessionFactory工厂的openSessionFromDataSource方法。首先获取环境信息,获取事务工厂(SpringManagedTransactionFactory),这个就是在MyBatis整合Spring的实现(1)中4.6创建的。还有Executor也是代理对象,把MyBatis中的配置的拦截器代理进来,这个以后再分析。
根据数据源(DataSource)创建了一个SpringManagedTransaction事务。
上图可以发现,这是个代理对象,最后new DefaultSqlSession对象。
最后通过方法反射调用DefaultSqlSession类的下图方法。
总结
MyBatis的SqlSession是在Dao层获取的,Service层只是Spring管理了事务,Dao层MyBatis通过与Spring的整合,来使用Spring的事务,所以目前的配置与Spring的整合,只能支持默认的SIMPLE类型(每次执行一个SQL都需要数据库编译),如果需要使用BATCH(批量新增,只需编译一次SQL,本次添加的SQL都按编译时的SQL来进行添加),这样明显提升效率。批量时,也想使用Spring的事务管理,就需要扩展SqlSession,这里以后再分析。