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.
- 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
- 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:
- I can do a facet of my own, dedicated to session closure
- Mybatis provides us with an upgraded version of DefaultSqlSession, which SqlSessionManager can solve
Thread security issues:
- Since we can't share it, it's natural that we get one from SqlSessionFactory every time we use DefaultSqlSession.
- 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.