Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Transaction Management of MyBatis

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "the transaction management mode of MyBatis". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "the transaction management mode of MyBatis".

1. Running environment Enviroment

When MyBatis is combined with different applications, different transaction management mechanisms are needed. When combined with Spring, the transaction is managed by Spring; when used alone, it needs to manage the transaction itself, and it may be managed by the container when running in the container.

MyBatis represents the runtime environment in Enviroment, which encapsulates three attributes:

The configuration of public class Configuration {/ / a MyBatis corresponds to only one environment protected Environment environment; / / other properties.} public final class Environment {private final String id; private final TransactionFactory transactionFactory; private final DataSource dataSource;}

two。 Transaction abstraction

MyBatis abstracts transaction management out of the Transaction interface, which is created by the implementation class of the TransactionFactory interface.

Public interface Transaction {Connection getConnection () throws SQLException; void commit () throws SQLException; void rollback () throws SQLException; void close () throws SQLException; Integer getTimeout () throws SQLException;} public interface TransactionFactory {void setProperties (Properties props); Transaction newTransaction (Connection conn); Transaction newTransaction (DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);}

The implementation of Executor holds a SqlSession implementation, and transaction control is delegated to SqlSession methods.

Public abstract class BaseExecutor implements Executor {protected Transaction transaction; public void commit (boolean required) throws SQLException {if (closed) {throw new ExecutorException ("Cannot commit, transaction is already closed");} clearLocalCache (); flushStatements (); if (required) {transaction.commit ();}} public void rollback (boolean required) throws SQLException {if (! closed) {try {clearLocalCache (); flushStatements (true) } finally {if (required) {transaction.rollback ();} / / omit other methods and properties}

3. Transaction management integrated with Spring

3.1Configuring TransactionFactory

When integrating with Spring, initialize the MyBatis through SqlSessionFactoryBean.

Protected SqlSessionFactory buildSqlSessionFactory () throws IOException {Configuration configuration; XMLConfigBuilder xmlConfigBuilder = null; if (this.configuration! = null) {configuration = this.configuration; if (configuration.getVariables () = = null) {configuration.setVariables (this.configurationProperties);} else if (this.configurationProperties! = null) {configuration.getVariables () .putAll (this.configurationProperties) }} else if (this.configLocation! = null) {xmlConfigBuilder = new XMLConfigBuilder (this.configLocation.getInputStream (), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration ();} else {configuration = new Configuration (); configuration.setVariables (this.configurationProperties);} if (this.objectFactory! = null) {configuration.setObjectFactory (this.objectFactory);} if (this.objectWrapperFactory! = null) {configuration.setObjectWrapperFactory (this.objectWrapperFactory) } if (this.vfs! = null) {configuration.setVfsImpl (this.vfs);} if (hasLength (this.typeAliasesPackage)) {String [] typeAliasPackageArray = tokenizeToStringArray (this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan: typeAliasPackageArray) {configuration.getTypeAliasRegistry (). RegisterAliases (packageToScan, typeAliasesSuperType = = null? Object.class: typeAliasesSuperType);}} if (! isEmpty (this.typeAliases)) {for (Class typeAlias: this.typeAliases) {configuration.getTypeAliasRegistry () .registerAlias (typeAlias);}} if (! isEmpty (this.plugins)) {for (Interceptor plugin: this.plugins) {configuration.addInterceptor (plugin) }} if (hasLength (this.typeHandlersPackage)) {String [] typeHandlersPackageArray = tokenizeToStringArray (this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packageToScan: typeHandlersPackageArray) {configuration.getTypeHandlerRegistry (). Register (packageToScan);}} if (! isEmpty (this.typeHandlers)) {for (TypeHandler typeHandler: this.typeHandlers) {configuration.getTypeHandlerRegistry () .register (typeHandler) }} if (this.databaseIdProvider! = null) {/ / fix # 64 set databaseId before parse mapper xmls try {configuration.setDatabaseId (this.databaseIdProvider.getDatabaseId (this.dataSource));} catch (SQLException e) {throw new NestedIOException ("Failed getting a databaseId", e);}} if (this.cache! = null) {configuration.addCache (this.cache);} if (xmlConfigBuilder! = null) {try {xmlConfigBuilder.parse () } catch (Exception ex) {throw new NestedIOException ("Failed to parse config resource:" + this.configLocation, ex);} finally {ErrorContext.instance (). Reset ();} / create SpringManagedTransactionFactory if (this.transactionFactory = = null) {this.transactionFactory = new SpringManagedTransactionFactory ();} / / encapsulated into Environment configuration.setEnvironment (new Environment (this.environment, this.transactionFactory, this.dataSource)) If (! isEmpty (this.mapperLocations)) {for (Resource mapperLocation: this.mapperLocations) {if (mapperLocation = = null) {continue;} try {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder (mapperLocation.getInputStream (), configuration, mapperLocation.toString (), configuration.getSqlFragments ()); xmlMapperBuilder.parse () } catch (Exception e) {throw new NestedIOException ("Failed to parse mapping resource:'" + mapperLocation + ", e);} finally {ErrorContext.instance (). Reset ();}} else {} return this.sqlSessionFactoryBuilder.build (configuration);} public class SqlSessionFactoryBuilder {public SqlSessionFactory build (Configuration config) {return new DefaultSqlSessionFactory (config);}}

The point is that when building a MyBatis Configuration object, the transactionFactory is configured as SpringManagedTransactionFactory and then encapsulated as an Environment object.

3.2 Runtime transaction management

The proxy object of Mapper holds SqlSessionTemplate, which implements the SqlSession interface.

Instead of calling a specific SqlSession method directly, SqlSessionTemplate's method is delegated to a dynamic proxy to intercept the method call through the proxy SqlSessionInterceptor.

SqlSessionInterceptor is responsible for getting the real SqlSession implementation associated with the database and deciding to commit or roll back the transaction and close the session after the method has been executed.

Public class SqlSessionTemplate implements SqlSession, DisposableBean {private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; 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 / / because there are many methods declared by the SqlSession API, / / it is troublesome to add transaction-related interceptions to each method. / / it is better to create an internal proxy object for unified processing. This.sqlSessionProxy = (SqlSession) newProxyInstance (SqlSessionFactory.class.getClassLoader (), new Class [] {SqlSession.class}, new SqlSessionInterceptor ());} public int update (String statement) {/ / execute method call return this.sqlSessionProxy.update (statement) on the proxy object } / / intercept method calls and add transaction control logic private class SqlSessionInterceptor implements InvocationHandler {public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {/ / to get the session SqlSession sqlSession = SqlSessionUtils.getSqlSession (SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator) associated with the database Try {/ / execute SQL operation Object result = method.invoke (sqlSession, args); if (! SqlSessionUtils.isSqlSessionTransactional (sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {/ / if the sqlSession is not managed by Spring, commit the transaction sqlSession.commit (true);} return result;} catch (Throwable t) {Throwable unwrapped = unwrapThrowable (t) 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 unwrapped } finally {if (sqlSession! = null) {SqlSessionUtils.closeSqlSession (sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}

SqlSessionUtils encapsulates access to the Spring transaction management mechanism.

/ / SqlSessionUtilspublic static SqlSession getSqlSession (SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {/ / get the session associated with the current transaction from Spring's transaction management mechanism SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource (sessionFactory); SqlSession session = sessionHolder (executorType, holder); if (session! = null) {/ / reuse return session;} / / create a new session session = sessionFactory.openSession (executorType) / register with Spring transaction management mechanism registerSessionHolder (sessionFactory, executorType, exceptionTranslator, session); return session;} private static void registerSessionHolder (SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive ()) {Environment environment = sessionFactory.getConfiguration (). GetEnvironment (); if (environment.getTransactionFactory () instanceof SpringManagedTransactionFactory) {holder = new SqlSessionHolder (session, executorType, exceptionTranslator); TransactionSynchronizationManager.bindResource (sessionFactory, holder) / / important: register the callback hook for session management. The real closing action is completed in the callback. TransactionSynchronizationManager.registerSynchronization (new SqlSessionSynchronization (holder, sessionFactory)); holder.setSynchronizedWithTransaction (true); / / maintain the reference count of sessions holder.requested ();} else {if (TransactionSynchronizationManager.getResource (environment.getDataSource ()) = = null) {} else {throw new TransientDataAccessResourceException ("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization") } else {}} public static void closeSqlSession (SqlSession session, SqlSessionFactory sessionFactory) {/ / get the session managed by Spring from the thread local variable SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource (sessionFactory); if ((holder! = null) & & (holder.getSqlSession () = = session)) {/ / Spring is not directly closed, holder.released () is closed by callback hook } else {/ / non-Spring managed direct shutdown session.close ();}}

SqlSessionSynchronization is an internal private class of SqlSessionUtils that acts as a callback hook to coordinate with Spring's transaction management mechanism, and TransactionSynchronizationManager calls back its methods when appropriate.

Private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {private final SqlSessionHolder holder; private final SqlSessionFactory sessionFactory; private boolean holderActive = true; public SqlSessionSynchronization (SqlSessionHolder holder, SqlSessionFactory sessionFactory) {this.holder = holder; this.sessionFactory = sessionFactory;} public int getOrder () {return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER-1;} public void suspend () {if (this.holderActive) {TransactionSynchronizationManager.unbindResource (this.sessionFactory) }} public void resume () {if (this.holderActive) {TransactionSynchronizationManager.bindResource (this.sessionFactory, this.holder);}} public void beforeCommit (boolean readOnly) {if (TransactionSynchronizationManager.isActualTransactionActive ()) {try {this.holder.getSqlSession () .commit () } catch (PersistenceException p) {if (this.holder.getPersistenceExceptionTranslator ()! = null) {DataAccessException translated = this.holder.getPersistenceExceptionTranslator () .translateExceptionIfPossible (p); if (translated! = null) {throw translated;}} throw p } public void beforeCompletion () {if (! this.holder.isOpen ()) {TransactionSynchronizationManager.unbindResource (sessionFactory); this.holderActive = false; / / actually close the database session this.holder.getSqlSession (). Close ();}} public void afterCompletion (int status) {if (this.holderActive) {TransactionSynchronizationManager.unbindResourceIfPossible (sessionFactory); this.holderActive = false / / actually close the database session this.holder.getSqlSession () .close ();} this.holder.reset ();}}

3.3 create a new session

/ / DefaultSqlSessionFactoryprivate SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null; try {final Environment environment = configuration.getEnvironment (); / / get transaction factory implementation final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment (environment); tx = transactionFactory.newTransaction (environment.getDataSource (), level, autoCommit); final Executor executor = configuration.newExecutor (tx, execType); return new DefaultSqlSession (configuration, executor, autoCommit);} catch (Exception e) {closeTransaction (tx) / / may have fetched a connection so lets call close () throw ExceptionFactory.wrapException ("Error opening session. Cause: "+ e, e);} finally {ErrorContext.instance (). Reset ();}} private TransactionFactory getTransactionFactoryFromEnvironment (Environment environment) {if (environment = = null | | environment.getTransactionFactory () = = null) {return new ManagedTransactionFactory ();} return environment.getTransactionFactory ();}

4. Summary

Executor, the core component of MyBatis, controls transactions through the Transaction interface.

When integrating with Spring, initializing Configuration sets transactionFactory to an instance of SpringManagedTransactionFactory.

The SqlSession injected into each Mapper agent is an instance of SqlSessionTemplate, which implements the SqlSession interface

SqlSessionTemplate delegates the method calls declared in the SqlSession interface to an internal dynamic proxy whose method handler is the inner class SqlSessionInterceptor.

When SqlSessionInterceptor receives a method call, it accesses Spring's transaction facility through SqlSessionUtil and reuses it if there is a SqlSession associated with the current transaction of Spring; if not, one is created.

SqlSessionInterceptor decides whether to commit or roll back the transaction based on the status of the current transaction in Spring. The real closure of the session is achieved through a callback hook registered on the TransactionSynchronizationManager.

Thank you for your reading, the above is the content of "the transaction management mode of MyBatis". After the study of this article, I believe you have a deeper understanding of the transaction management mode of MyBatis, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report