Mybtais
在同一个方法中,Mybatis多次请求数据库是否要创建多个sqlsession会话
- 在加了注解的事务的情况下,Mapper的每次请求数据库都会创建一个sqlsession与数据库交互
- 在加了事务的方法中,多次请求只创建一个sqlsession。
什么是SqlSession?
sqlsession是Mybatis工作的最顶层API会话接口,所有的数据库操作都由它实现。一个Sqlsession对应一次数据库会话,它不是永久存活,每次访问数据库的时候进行创建。一个SqlSession应仅存活于一个业务请求当中。
SqlSession是否是线程安全的?
SqlSession不是线程安全的,每个线程都应该有它的Sqlsession,不能将一个SqlSession搞成单例模式,或者静态域和实例变量的形式都会导致sqlsession出现事务问题。
SqlSession的创建过程:
- 从Configuration配置拿到Environment数据源
- 从数据源中获取TransactionFactory和DataSource并且创建一个Transaction连接管理对象
- 创建Executor对象(JDBC的操作封装)
- 创建Sqlsession会话。
由此证明创建一个SqlSession,都会创建一个sqlsession的连接管理对象。如果出现Sqlsession共享,则Transaction会出问题。
从源码的角度分析
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());
}
从源码当中可以看到此处采用了jdk的动态代理,实际处理的是SqlSessionInterceptor
```java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取sqlsession 该session可能是上一次的 也可能是新建的 SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
//执行mapper方法
Object result = method.invoke(sqlSession, args);
//判断Sqlsession是否是一个事务 如果不是一个事务则commit 如果是一个事务 则不commit
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;
}
> Mapper的所有方法最终都会用这个方法来完成数据库的所有操作。
> ```java
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
//TransactionSynchronizationManager 是Spring的一个事务管理器 将资源存储到当前线程中
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
session = sessionFactory.openSession(executorType);
//
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
```java private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; //Spring TX 是否激活 判断事务管理器是否激活的条件喂 synchronizations的事务管理器是否为空 if (TransactionSynchronizationManager.isSynchronizationActive()) { //获取Enviorment环境 Environment environment = sessionFactory.getConfiguration().getEnvironment(); //当前的环境是否是Spring事务管理工厂进行管理 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { LOGGER.debug(() -> “Registering transaction synchronization for SqlSession [“ + session + “]”); holder = new SqlSessionHolder(session, executorType, exceptionTranslator); // 绑定当前SqlSessionHolder到线程ThreadLocal中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); // // 注册SqlSession同步回调器 TransactionSynchronizationManager .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); //会话次数+1 holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { 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 {
LOGGER.debug(() -> "SqlSession [" + session
+ "] was not registered for synchronization because synchronization is not active");
}
}
>
//当程序启动的时候 执行 该作用当前线程激活事务。 由事务管理上的事务开始时调用 public static void initSynchronization() throws IllegalStateException { if (isSynchronizationActive()) { throw new IllegalStateException(“Cannot activate transaction synchronization - already active”); } logger.trace(“Initializing transaction synchronization”); synchronizations.set(new LinkedHashSet<>()); }
> //Mybatis自定义实现了一个事务同步回调器,在注册SqlSession的同时会将SqlSessionSynchronization注册到当前线程事务管理器中,它的作用是根据事务的完成状态回调来处理线程资源,即当前如果有事务,那么每次状态发生就回调用事务同步器。
> ```java
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (holder != null) && (holder.getSqlSession() == session);
}
取决于当前SqlSession是否为空并且判断当前SqlSession是否与ThreadLocal的SqlSession相等。如果当前没有事务,SqlSession是不会保存到事务管理器,没有事务。会话提交
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if ((holder != null) && (holder.getSqlSession() == session)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
}
holder.released();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
}
session.close();
}
}
Spring的自定义事务的一些机制,当前有事务时,会初始化当前线程事务管理器的synchronizations,激活了当前线程同步管理器,当Mybatis访问数据库会首先从当前线程事务管理器获取SqlSession,如果不存在就会创建一个会话,接着注册会话到当前线程事务管理器中,如果当前有事务,则会话不关闭也不commit,Mybatis还自定义了一个TransactionSynchronization,用于事务每次状态发生时回调处理