基本概念
- SqlSession:
- 数据库CRUD及事务操作接口
- 线程不安全,常用于Request范围或method范围
// Request范围,4次sql执行共用一个SqlSession
sqlSessionManager.startManagedSession();
try {
sqlSessionManager.query1();
sqlSessionManager.query2();
sqlSessionManager.update1();
sqlSessionManager.update2();
//...
}catch (Throwable t) {
sqlSessionManager.rollback();
} finally {
sqlSessionManager.close();
}
// 方法范围,两次调用创建了两个session
SqlSessionManager.query1();
SqlSessionManager.query2();
- SqlSessionFactory
- 创建SqlSession的工厂类
- SqlSessionManager/SqlSessionTemplate[装饰器模式]
- SqlSession装饰器
- 增强SqlSession生命周期管理
SqlSessionManager/SqlSessionTemplate源码分析
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
// SqlSession动态代理,增强SqlSession生命周期管理
private final SqlSession sqlSessionProxy;
// 线程局部变量localSqlSession,每个线程内部都持有一个sqlSession副本,互不干扰
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
// 创建代理对象SqlSessionProxy
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
// 如果线程内已存在sqlSession,直接共用,方法调用后由程序员手动关闭
try {
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
// 1. 创建方法级sqlSession
final SqlSession autoSqlSession = openSession();
try {
// 2. 调用SqlSession方法执行sql
final Object result = method.invoke(autoSqlSession, args);
// 3.1 执行成功,提交事务
autoSqlSession.commit();
return result;
} catch (Throwable t) {
// 3.2 执行异常,回滚事务
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
// 4. 关闭SqlSession
autoSqlSession.close();
}
}
}
}
}
/**
* 1. Thread safe, Spring managed, {@code SqlSession} that works with Spring
* transaction management to ensure that that the actual SqlSession used is the
* one associated with the current Spring transaction.
* spring管理,线程安全,确保SqlSession是当前spring事务的sqlSession
* 2. SqlSession生命周期管理,包括commit, rollback, close
* 3. 单例,所有DAO可以共用一个
*/
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());
}
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);
// 非事务的sqlSession,执行完立即commit
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
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);
}
}
}
}
}
/**
* 1. 非事务SqlSession,close
* 2. 事务sqlsession, 更新引用计数器,当计数器为0,即事务结束时,spring调用close callback
*/
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
......
}
- Spring SqlSessionTemplate SqlSession生命周期管理
- SqlSessionTemplate与SqlSessionManager实现SqlSession生命周期管理的方式相同
- SqlSessionManager由使用者决定是否共用SqlSession
- SqlSessionTemplate由Spring事务管理器决定是否共用SqlSession。
- 事务内部共用SqlSession
- 非事务不共用SqlSession
Spring创建SqlSessionFactory, SqlSessionTemplate, SqlSession流程
spring配置
<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="*.dao" />
</bean>
MapperProxy
- DAO动态代理类:拦截DAO方法调用,调用SqlSession执行SQL,返回结果
- Spring中采用SqlSessionTemplate执行SQL,对SqlSession的生命周期管理进行增强
// 拦截器
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行sql,处理结果,并返回
return mapperMethod.execute(sqlSession, args);
}
......
}
// MapperProxyFactory newInstance()生成动态代理对象
Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, new MapperProxy<T>(sqlSession, mapperInterface, methodCache))
JAVA动态代理
- 详见代理模式(暂无)