Differences between Mybatis Source Series 3-3 SqlSession s

Posted by LearningKid on Tue, 31 Dec 2019 03:04:33 +0100

No matter when you start, it's important not to stop after you start

Three SqlSession s

DefaultSqlSession and SqlSessionManager and SqlSessionTemplate are three common sqlsesion s I have

From the class diagram, you can see that all three of them have implemented SqlSession, that is, they can all represent a session.Unlike others, SqlSessionManager implements SqlSessionFactory

What is the difference between the three sqlsession s?What are their scenarios?

We start with DefaultSqlSession.

DefaultSqlSession

DefaultSqlSession is the default implementation of SqlSession.

When we use Mybatis alone, we usually use DefaultSqlSession to execute SQL and manipulate databases.

  String resource = "mybatis-config.xml";
          // Read Configuration File
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // Building sqlSessionFactory
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          // Get sqlSession
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
             User user = sqlSession.selectOne("MyMapper.selectUser", 1);
             System.out.println(user);
          } finally {
             sqlSession.close();
    	}

However, DefaultSqlSession has two shortcomings.

  1. We need to turn off sqlsesion manually by ourselves, and we know that people are always unreliable.Forget about sqlsession is very likely to happen
  2. Thread security issues: DefaultSqlSession is a thread-insecure Sqlsession.That is, DefaultSqlSession cannot be a singleton.

How do I solve these two problems?

Automatically close Session issue:

  1. I can do a facet of my own, dedicated to session closure
  2. Mybatis provides us with an upgraded version of DefaultSqlSession, which SqlSessionManager can solve

Thread security issues:

  1. Since we can't share it, it's natural that we get one from SqlSessionFactory every time we use DefaultSqlSession.
  2. But what if we still want to use a single version of Sqlsession?Don't panic, Mybatis has provided us with an upgraded version of Default SqlSession, which can be solved by SqlSession Manager.

SqlSessionManager

Personally, SqlSessionManager is not an upgraded version of DefaultSqlSession, but rather a proxy (or encapsulated) version of DefaultSqlSession.

Why do you say that?Let's see what SqlSessionManager can do

1. Ability to acquire DefaultSqlSession

	String resource = "mybatis-config.xml";
    // Read Configuration File
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionManager sqlSessionManager =SqlSessionManager.newInstance(inputStream);
    //Get a SqlSession
    SqlSession session = sqlSessionManager.openSession();
    
    //openSession of the SqlSessionManager class
      public SqlSession openSession() {
        return sqlSessionFactory.openSession();
      }

From this point of view, he is actually no different from SqlSessionFactory.He just encapsulated the SqlSessionFactory and left it to SqlSessionFactory to do the work. sqlSessionManager.openSession() = sqlSessionFactory.openSession()

The resulting Sqlsession is also, naturally, DefaultSqlSession

2. Solve DefaultSqlSession's shortcomings

2.1 Solve the automatic shutdown problem

To solve the auto-shutdown problem, SqlSessionManager uses proxy technology to achieve auto-shutdown.

Using JDK dynamic proxy technology, the proxy object sqlSessionProxy is generated dynamically, and the SqlSessionInterceptor internal class is used to enhance the execution method of SqlSession.

    private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
        //Generate a proxy object using JDK proxy technology
        this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[]{SqlSession.class},
            new SqlSessionInterceptor());
      }

Let's look at insert as an example

    sqlSessionManager.insert()
    
    public int insert(String statement) {
        return sqlSessionProxy.insert(statement);
    }

Inside sqlSessionManager is the insert method that calls the sqlsessionProxy proxy object when the insert() method is executed.The SqlSessionInterceptor#invoke method of the enhancer is then executed.

    @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
          if (sqlSession != null) {// When using thread local variables
            try {
              return method.invoke(sqlSession, args);
            } catch (Throwable t) {
              throw ExceptionUtil.unwrapThrowable(t);
            }
          } else {//Thread local variables are not used.
              //Get a DefaultSqlSession from sqlSessionFactory
            final SqlSession autoSqlSession = openSession();
            try {
              final Object result = method.invoke(autoSqlSession, args);
              autoSqlSession.commit();//Submit
              return result;
            } catch (Throwable t) {
              autoSqlSession.rollback();//RollBACK
              throw ExceptionUtil.unwrapThrowable(t);
            } finally {
              autoSqlSession.close();//Close sqlsession
            }
          }
        }
      }

Inside the invoke method, call openSession() to get a DefaultSqlSession from sqlSessionFactory, execute the corresponding method, and execute close sqlsession in finally

Last Execution Sequence

Back of sqlSessionManager.insert() is still DefaultSqlSession.insert.And helped us close DefaultSqlSession.

Developers don't have to worry anymore about closing DefaultSqlSession.

When executing the sqlsession method of sqlSessionManager, the essence is to create DefaultSqlSession every time. Is it not thread safe?

The singleton is sqlSessionManager; but the real execution is DefaultSqlSession

2.1 Solve thread security issues.

Another way to resolve thread security issues is for sqlSessionManager to use thread local variables instead of retrieving a DefaultSqlSession every time a CURD operation is performed.Thread local variables use the same request within a thread, which saves time in creating DefaultSqlSession and is thread safe.

Use:

    sqlSessionManager.startManagedSession();//Bind Session to Thread Local Variable
    sqlSessionManager.insert()

Principle:

    private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
    
    public void startManagedSession() {
        this.localSqlSession.set(openSession());
    }

When startManagedSession() starts a thread local variable, it gets a session from sqlSessionFactory, puts it into the thread local SqlSession, and binds it to the current thread.

When we execute the sqlSessionManager.insert method, the invoke method to the enhancer gets the sqlsession bound to the current thread from the local SqlSession.

    @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //Get from thread local variable
          final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
          if (sqlSession != null) {
            try {
              return method.invoke(sqlSession, args);
            } catch (Throwable t) {
              throw ExceptionUtil.unwrapThrowable(t);
            }
          }

Summary: We can see that SqlSessionManager upgraded the use of DefaultSqlSession through dynamic proxy technology + thread local variables.

SqlSessionTemplate

SqlSessionTemplate is a thread-safe sqlsession when Mybatis is integrated with Spring.

Look at its construction

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
    
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
      }

You'll find that he's similar to SqlSessionManager, which uses JDK dynamic proxy technology.SqlSessionTemplate also has an internal class enhancer, SqlSessionInterceptor.

    private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Get Connections
          SqlSession sqlSession = getSqlSession(
              SqlSessionTemplate.this.sqlSessionFactory,
              SqlSessionTemplate.this.executorType,
              SqlSessionTemplate.this.exceptionTranslator);
          try {
          //Execute sqlsession target method
            Object result = method.invoke(sqlSession, args);
            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);
            }
          }
        }
      }

We also look at SqlSessionTemplate from the perspective of automatic shutdown and thread security

1. Solve thread security issues

Unlike SqlSessionManager's own way of managing sessions, SqlSessionTemplate outsources session management

SqlSessionTemplate gives SqlSessionUtils the job of getting sqlsession.

 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    //Get Sqlsession from Transaction Synchronizer.
    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;
  }

SqlSessionUtils will try to get sqlsesion from TransactionSynchronization Manager Transaction Synchronization Manager first, but not from the factory.

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    
        SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);//Get sqlsession from transaction synchronizer
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
          return session;
        }
        session = sessionFactory.openSession(executorType);
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
    }

TransactionSynchronizationManager In spring Source Series 11: Execution of Transaction Agent Objects Mentioned in one article.Is an important component of the spring transaction implementation principle.

TransactionSynchronization Manager itself is a thread local variable manager.

From this point of view, he is the same as SqlSessionManager.Just manage differently, one by oneself, one by outsourcing

2. Solve the automatic shutdown problem

Like SqlSessionManager, it also helps close after a session has been executed. The difference is,

  • If session is managed by TransactionSynchronization Manager, only the reference counter is updated so that Spring calls the close callback and closes the session at the end of a managed transaction.
  • If session is not managed by TransactionSynchronization Manager, close session directly
 public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
  
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    if ((holder != null) && (holder.getSqlSession() == session)) {
     //Count minus 1
      holder.released();
    } else {
    //Close session
      session.close();
    }
  }

summary

  • DefaultSqlSession and SqlSessionManager are two sqlsesions provided by Mybatis by default; SqlSessionTemplate is the sqlsesion used when Mybatis is integrated with Spring
  • DefaultSqlSession is single thread insecure, SqlSessionManager and SqlSessionTemplate are single thread safe
  • Both SqlSessionManager and SqlSessionTemplate have optimized the automatic closure of DefaultSqlSession via dynamic proxy technology
  • SqlSessionManager manages sqlsession by itself and SqlSessionTemplate is outsourced to TransactionSynchronization Manager to manage sqlsession.

If there are any errors in this article, please criticize and teach us. Thank you!

WeChat Public Number: Source Action Enjoy the source code, take action, source code action

Welcome to my Public Number [Source Action]. The latest personal understanding will be delivered in time.

Topics: Programming Session Mybatis Spring JDK