SqlSessionFactoryBean
配置
<bean id="dataSources" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="$jdbc.password"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSources"/>
<property name="mapperLocations" value="classpath:mapper/*.mapper.xml"/>
</bean>
SqlSessionFactoryBean 介绍
每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 。 SqlSessionFactory 对 象 的 实 例 可 以 通 过 SqlSessionFactoryBuilder 对 象 来 获 得 。 SqlSessionFactoryBuilder 对象可以从 XML 配置文件,或从 Configuration 类的习惯准备的实 例中构建 SqlSessionFactory 对象
在Spring中,通常使用SqlSessionFactoryBean去创建一个共享的Mybatis(SqlSessionFactory)。
SqlSessionFactoryBean实现了两个接口:FactoryBean和InitializingBean。bean的创建过程中,少不了使用这两个接口,那么这两个接口的作用分别为:
FactoryBean:实现了此接口的类,通过getBean方法获取bean时其实是获取此类的getObject()返回的实例。
InitializingBean:实现此接口的bean会在初始化时调用其afterPropertieSet方法进行bean的逻辑初始化。
/**
* {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
* This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context;
* the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection.
*
* Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction
* demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions
* which span multiple databases or when container managed transactions (CMT) are being used.
*
* @author Putthibong Boonbong
* @author Hunter Presnall
* @author Eduardo Macarron
* @author Eddú Meléndez
* @author Kazuki Shimizu
*
* @see #setConfigLocation
* @see #setDataSource
*/
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
}
SqlSessionFactroyBean创建过程:
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
查看buildSqlSessionFactory方法的过程,其本质上与独立的Mybati创建过程无异,都是通过SqlSessionFactoryBuilder与Configuration创建一个DefaultSqlSessionFactory实例。在通过FactoryBean的getObject方法去获取对象的单例时,获取到的是一个DefaultSqlSessionFactory的单例。
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
后面会补充讲解Mybatis是如何创建DefaultSqlSessionFactory。
MapperFactoryBean 介绍
<bean id="signInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface"
value="com.dao.SignInfoMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
同样实现了FactoryBean和InitializingBeangetObject方法
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
在独立的Mybatis我们也是通过SqlSession获取Mapper的代理对象,那么Spring是如何保证SqlSession在线程中的安全呢?getSqlSession方法获取到的又是什么样的对象呢?
通过分析MapperFactoryBean的类图,我们可以找知道getSession获取的其实是一个SqlSessionTemplate的对象,传递给Mapper代理工厂MapperProxy的也是此对象,那么在MapperProxy对Mapper进行方法代理时,会通过SqlSessionTemplate去执行相应的CRUD。看一下SqlSessionTemplate在执行insert时,是如何实现的。
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
这个sqlSessionProxy是什么,什么时候进行初始化的?
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
}
在sqlSessionProxy中通过JDK动态代理去给sqlSessionProxy进行初始化,调用sqlSessionProxy时,会调用SqlSessionInterceptor的invoker方法。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
getSqlSession会为每一个线程分配一个SqlSession确保其在多线程环境下的安全性。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}