您好,登錄后才能下訂單哦!
這篇文章主要講解了“MyBatis的事務管理方式”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“MyBatis的事務管理方式”吧!
1. 運行環境 Enviroment
當 MyBatis 與不同的應用結合時,需要不同的事務管理機制。與 Spring 結合時,由 Spring 來管理事務;單獨使用時需要自行管理事務,在容器里運行時可能由容器進行管理。
MyBatis 用 Enviroment 來表示運行環境,其封裝了三個屬性:
public class Configuration { // 一個 MyBatis 的配置只對應一個環境 protected Environment environment; // 其他屬性 ..... } public final class Environment { private final String id; private final TransactionFactory transactionFactory; private final DataSource dataSource; }
2. 事務抽象
MyBatis 把事務管理抽象出 Transaction 接口,由 TransactionFactory 接口的實現類負責創建。
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); }
Executor 的實現持有一個 SqlSession 實現,事務控制是委托給 SqlSession 的方法來實現的。
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(); } } } } // 省略其他方法、屬性 }
3. 與 Spring 集成的事務管理
3.1 配置 TransactionFactory
與 Spring 集成時,通過 SqlSessionFactoryBean 來初始化 MyBatis 。
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(); } } // 創建 SpringManagedTransactionFactory if (this.transactionFactory == null) { this.transactionFactory = new SpringManagedTransactionFactory(); } // 封裝成 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); } }
重點是在構建 MyBatis Configuration 對象時,把 transactionFactory 配置成 SpringManagedTransactionFactory,再封裝成 Environment 對象。
3.2 運行時事務管理
Mapper 的代理對象持有的是 SqlSessionTemplate,其實現了 SqlSession 接口。
SqlSessionTemplate 的方法并不直接調用具體的 SqlSession 的方法,而是委托給一個動態代理,通過代理 SqlSessionInterceptor 對方法調用進行攔截。
SqlSessionInterceptor 負責獲取真實的與數據庫關聯的 SqlSession 實現,并在方法執行完后決定提交或回滾事務、關閉會話。
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; // 因為 SqlSession 接口聲明的方法也不少, // 在每個方法里添加事務相關的攔截比較麻煩, // 不如創建一個內部的代理對象進行統一處理。 this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); } public int update(String statement) { // 在代理對象上執行方法調用 return this.sqlSessionProxy.update(statement); } // 對方法調用進行攔截,加入事務控制邏輯 private class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 獲取與數據庫關聯的會話 SqlSession sqlSession = SqlSessionUtils.getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { // 執行 SQL 操作 Object result = method.invoke(sqlSession, args); if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // 如果 sqlSession 不是 Spring 管理的,則要自行提交事務 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 封裝了對 Spring 事務管理機制的訪問。
// SqlSessionUtils public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { // 從 Spring 的事務管理機制那里獲取當前事務關聯的會話 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { // 已經有一個會話則復用 return session; } // 創建新的 會話 session = sessionFactory.openSession(executorType); // 注冊到 Spring 的事務管理機制里 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); // 重點:注冊會話管理的回調鉤子,真正的關閉動作是在回調里完成的。 TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); // 維護會話的引用計數 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) { // 從線程本地變量里獲取 Spring 管理的會話 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if ((holder != null) && (holder.getSqlSession() == session)) { // Spring 管理的不直接關閉,由回調鉤子來關閉 holder.released(); } else { // 非 Spring 管理的直接關閉 session.close(); } }
SqlSessionSynchronization 是 SqlSessionUtils 的內部私有類,用于作為回調鉤子與 Spring 的事務管理機制協調工作,TransactionSynchronizationManager 在適當的時候回調其方法。
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; // 真正關閉數據庫會話 this.holder.getSqlSession().close(); } } public void afterCompletion(int status) { if (this.holderActive) { TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory); this.holderActive = false; // 真正關閉數據庫會話 this.holder.getSqlSession().close(); } this.holder.reset(); } }
3.3 創建新會話
// DefaultSqlSessionFactory private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); // 獲取事務工廠實現 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. 小結
MyBatis 的核心組件 Executor 通過 Transaction 接口來進行事務控制。
與 Spring 集成時,初始化 Configuration 時會把 transactionFactory 設置為 SpringManagedTransactionFactory 的實例。
每個 Mapper 代理里注入的 SqlSession 是 SqlSessionTemplate 的實例,其實現了 SqlSession 接口;
SqlSessionTemplate 把對 SqlSession 接口里聲明的方法調用委托給內部的一個動態代理,該代理的方法處理器為內部類 SqlSessionInterceptor 。
SqlSessionInterceptor 接收到方法調用時,通過 SqlSessionUtil 訪問 Spring 的事務設施,如果有與 Spring 當前事務關聯的 SqlSession 則復用;沒有則創建一個。
SqlSessionInterceptor 根據 Spring 當前事務的狀態來決定是否提交或回滾事務。會話的真正關閉是通過注冊在 TransactionSynchronizationManager 上的回調鉤子實現的。
感謝各位的閱讀,以上就是“MyBatis的事務管理方式”的內容了,經過本文的學習后,相信大家對MyBatis的事務管理方式這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。