Deep underlying source code, parsing Mybatis session mechanism

Point of attention, do not get lost; Java-related technologies and continuously updated information! ! !

In the same method, Mybatis repeated requests to the database, if you want to create multiple SqlSession sessions?

Line and more recently may, at that time my mind a blur, eyes blurred, although I had answered him: If multiple requests for the same transaction, so multiple requests are sharing a SqlSession, on the contrary every request to create a SqlSession. This is what we are accustomed to in the normal development of common sense, but I was not from the perspective of the principle of analysis, leading to Chafanbusi, as older drivers, I feel deep remorse.

When satisfied with the test run a demo without transaction method, each request whether it will create a SqlSession:

Here Insert Picture Description
As can be seen from the log, in the absence of added affairs is indeed the case of each request Mapper database, creates a SqlSession interact with the database, let's look at the plus transaction:
Here Insert Picture Description
As can be seen from the log, in the method Canada after the transaction, only two requests have created a SqlSession, proving once again that my answer above, but only so the answer is not entirely reflect an older driver should have professionalism, so I want to start up.

What is SqlSession

Before departure, we have to first thoroughly understand what is SqlSession?

In simple terms, is the top SqlSession API interfaces Mybatis work session, all database operations to achieve through it, because it is a conversation that a SqlSession should survive only in a service request, it can be said that corresponds to a SqlSession a database session, it is not a permanent survival, we need to create it each time you access the database.

Therefore, SqlSession not thread-safe, each thread should have its own SqlSession instance, do not be a single example in the form of SqlSession mess, or static fields and instance variables of the form will cause problems SqlSession affairs, which is Why multiple requests will share a conversation SqlSession reason for the same transaction, we illustrate this point from the process of creating the SqlSession:

  • Environment Configuration get configuration data from the source class;
  • Get TransactionFactory DataSource and from the data source, and connected to create a Transaction management object;
  • Executor create objects (SqlSession just the facade of all operations, the real work is to be Executor, it encapsulates the underlying JDBC all the details of the operation);
  • Creating SqlSession session.
    Each time you create a SqlSession session, will be accompanied by a dedicated management object to create a connection SqlSession if SqlSession share, the transaction will be a problem.

From the point of view of the source

From which source code analysis as an entry step it? If it is written before I read several articles on mybatis that source code analysis, I believe you will not dawdle before Mybatis source, the delay can not find the entrance.

In the previous article, have said, Mapper implementation class is a proxy, the real execution logic is MapperProxy.invoke (), this method of execution is final sqlSessionTemplate.

org.mybatis.spring.SqlSessionTemplate:

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());
}

This construction method is to create the ultimate SqlSessionTemplate, you can see sqlSessionTemplate used in the SqlSession, is a dynamic proxy class SqlSessionInterceptor achieve, so we direct in-depth fortress:

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);
 }
 }
 }
}

Mapper all the way to eventually use this method to handle all database operations, integration mybatis my spring and mybatis alone Is there a difference, in fact, no difference, the difference is spring encapsulates all the details of the deal, you do not write a lot of redundancy Code focus on business development.

The dynamic proxy method mainly undertook the following process:

  • According to current conditions get a SqlSession, this time SqlSession may or may create new SqlSession is to get to the last request;

  • Performing SqlSession reflection method, and then determines whether the current session is a transaction, if a transaction, not commit;

  • If an exception is thrown at this time, if it is determined PersistenceExceptionTranslator not empty, then the current session is closed, and the set is empty sqlSession prevent repeated finally closed, spring PersistenceExceptionTranslator is defined data access exception integration layer interfaces;

  • finally execute no matter how the outcome, as long as the current session is not empty, then it will close the current session to perform the operation, the operation will close the current session of the current session transaction to determine whether there is a release or direct session closed.

org.mybatis.spring.SqlSessionUtils#getSqlSession:

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;
}

Is not seen you see when you log satisfied with running a demo "Creating a new SqlSession", then prove my place very direct in-depth and accurate, without the slightest error. In this method, which, first of all is to get from TransactionSynchronizationManager (hereinafter referred to as the current thread transaction manager) whether the current thread threadLocal have SqlSessionHolder, if you remove the current SqlSession from SqlSessionHolder, if the current thread threadLocal no SqlSessionHolder, created a SqlSession from sessionFactory in the creation of specific steps already said, and then registered in the session to the current thread threadLocal.

Let's look at the structure of the current thread's transaction manager:

public abstract class TransactionSynchronizationManager {
 // ...
 // 存储当前线程事务资源,比如Connection、session等
 private static final ThreadLocal<Map<Object, Object>> resources =
 new NamedThreadLocal<>("Transactional resources");
 // 存储当前线程事务同步回调器
 // 当有事务,该字段会被初始化,即激活当前线程事务管理器
 private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
 new NamedThreadLocal<>("Transaction synchronizations");
 // ...
}

This is the spring of the current thread a transaction manager, which allows current resources stored in ThreadLocal to the current thread, can also be seen from the front SqlSessionHolder is stored in resources in.

org.mybatis.spring.SqlSessionUtils#registerSessionHolder:

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
 PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
 SqlSessionHolder holder;
 // 判断当前是否有事务
 if (TransactionSynchronizationManager.isSynchronizationActive()) {
 Environment environment = sessionFactory.getConfiguration().getEnvironment();
 // 判断当前环境配置的事务管理工厂是否是SpringManagedTransactionFactory(默认)
 if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
 if (LOGGER.isDebugEnabled()) {
 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) {
 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");
 }
 }
}

Sign SqlSession to the current thread Transaction Manager is the first condition of the current environment there is a transaction, or not registered, to determine whether there is a transaction that ThreadLocal whether synchronizations are empty:

public static boolean isSynchronizationActive() {
 return (synchronizations.get() != null);
}

Whenever we open a transaction, calls initSynchronization () method to initialize synchronizations, to activate the current thread transaction manager.

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<TransactionSynchronization>());
}

So when the current transaction will be registered SqlSession to the current thread a ThreadLocal.

Mybatis himself realized the transaction synchronization callbacks is SqlSessionSynchronization a custom, while SqlSession registration, will be registered with the current thread SqlSessionSynchronization Transaction Manager, its role is to handle the callback thread resources based on completion of the transaction, that is, If there is current affairs, then when the condition occurs each time a transaction callback will synchronizer, the details can be the venue to the Spring org.springframework.transaction.support package.

Back to the logic SqlSessionInterceptor proxy class and found to determine whether to call the session to submit the following methods:

org.mybatis.spring.SqlSessionUtils#isSqlSessionTransactional:

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);
}

Depending on whether the current SqlSession empty and determine whether the current SqlSession ThreadLocal SqlSession equal in front also analyzed, if no transaction, SqlSession is not saved to the transaction synchronization manager, that there is no transaction, the session commit.

org.mybatis.spring.SqlSessionUtils#closeSqlSession:

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();
 }
}

Regardless of the method of how the results will need to do to close the session logic, where the judge is to determine whether the current transaction, if the transaction SqlSession them, reducing the number of citations is, there is no real closed session. If the current session transaction does not exist, directly close the session.

Written in the last

I am a Mybatis problem, but in the Spring trap, suddenly found the entire transaction links are in the Spring of control which, here involves a number of mechanisms Spring custom affairs, in which the current thread Transaction Manager is the core of the entire transaction and the central axis, the current time there is a transaction, initializes the current thread synchronizations transaction manager, which activates the current thread synchronization manager, when Mybatis access the database will first be obtained from the current thread transaction manager SqlSession, if there is no will to create a session, then the session to register the current thread Affairs Manager, if you currently have a transaction, the session is not closed nor commit, Mybatis also customize a TransactionSynchronization, the callback for processing transactions occur every state.

We feel that there is help too, then point a praise to encourage it!

Guess you like

Origin blog.csdn.net/XingXing_Java/article/details/91822074