{"id":2171,"date":"2021-07-26T22:09:33","date_gmt":"2021-07-26T14:09:33","guid":{"rendered":"https:\/\/coderbee.net\/?p=2171"},"modified":"2021-07-26T22:09:33","modified_gmt":"2021-07-26T14:09:33","slug":"spring-%e4%ba%8b%e5%8a%a1%e5%8e%9f%e7%90%86%e4%b8%8e%e9%9b%86%e6%88%90-mybatis-%e4%ba%8b%e5%8a%a1%e7%ae%a1%e7%90%86","status":"publish","type":"post","link":"https:\/\/coderbee.net\/index.php\/framework\/20210726\/2171","title":{"rendered":"Spring \u4e8b\u52a1\u539f\u7406\u4e0e\u96c6\u6210 MyBatis \u4e8b\u52a1\u7ba1\u7406"},"content":{"rendered":"<h1>1. \u4e8b\u52a1\u7ba1\u7406\u5668\u62bd\u8c61<\/h1>\n<p>\u4e00\u4e2a\u4e8b\u52a1\u7ba1\u7406\u5668\u53ea\u9700\u8981\u4e09\u4e2a\u57fa\u672c\u7684\u80fd\u529b\uff1a\u83b7\u53d6\u4e00\u4e2a\u4e8b\u52a1\u3001\u63d0\u4ea4\u4e8b\u52a1\u3001\u56de\u6eda\u4e8b\u52a1\u3002<\/p>\n<pre><code class=\"Java\">public interface PlatformTransactionManager extends TransactionManager {\n    \/\/ \u83b7\u53d6\u4e00\u4e2a\u4e8b\u52a1\n    TransactionStatus getTransaction(@Nullable TransactionDefinition definition)\n            throws TransactionException;\n\n    \/\/ \u63d0\u4ea4\u4e8b\u52a1\n    void commit(TransactionStatus status) throws TransactionException;\n\n    \/\/ \u56de\u6eda\u4e8b\u52a1\n    void rollback(TransactionStatus status) throws TransactionException;\n}\n<\/code><\/pre>\n<p><code>DataSourceTransactionManagerAutoConfiguration<\/code> \u914d\u7f6e\u7c7b\u5bfc\u5165\u4e86\u6570\u636e\u5e93\u4e8b\u52a1\u7ba1\u7406\u5668 <code>DataSourceTransactionManager<\/code>\u3002<\/p>\n<h1>2. \u4e8b\u52a1\u540c\u6b65\u56de\u8c03\u94a9\u5b50<\/h1>\n<p>\u4e8b\u52a1\u540c\u6b65\u56de\u8c03\u94a9\u5b50\u8ba9\u6211\u4eec\u6709\u673a\u4f1a\u5728\u4e8b\u52a1\u7684\u5404\u4e2a\u9636\u6bb5\u52a0\u5165\u4e00\u4e9b\u534f\u8c03\u7684\u52a8\u4f5c\u3002<\/p>\n<pre><code>public interface TransactionSynchronization extends Flushable {\n    \/** Completion status in case of proper commit. *\/\n    int STATUS_COMMITTED = 0;\n\n    \/** Completion status in case of proper rollback. *\/\n    int STATUS_ROLLED_BACK = 1;\n\n    \/** Completion status in case of heuristic mixed completion or system errors. *\/\n    int STATUS_UNKNOWN = 2;\n\n    default void suspend() {}\n    default void resume() {}\n    default void flush() {}\n    default void beforeCommit(boolean readOnly) {}\n    default void beforeCompletion() {}\n    default void afterCommit() {}\n    default void afterCompletion(int status) {}\n}\n<\/code><\/pre>\n<p><!--more--><\/p>\n<h1>3. TransactionSynchronizationManager<\/h1>\n<p><code>TransactionSynchronizationManager<\/code> \u59d4\u6258\u96c6\u4e2d\u7ba1\u7406\u4e0e\u6bcf\u4e2a\u7ebf\u7a0b\u7ed1\u5b9a\u7684\u8d44\u6e90\u548c\u4e8b\u52a1\u540c\u6b65\u94a9\u5b50\u3002<\/p>\n<pre><code>private static final ThreadLocal&lt;Map&lt;Object, Object&gt;&gt; resources =\n        new NamedThreadLocal&lt;&gt;(\"Transactional resources\");\n\nprivate static final ThreadLocal&lt;Set&lt;TransactionSynchronization&gt;&gt; synchronizations =\n        new NamedThreadLocal&lt;&gt;(\"Transaction synchronizations\");\n\nprivate static final ThreadLocal&lt;String&gt; currentTransactionName =\n        new NamedThreadLocal&lt;&gt;(\"Current transaction name\");\n\nprivate static final ThreadLocal&lt;Boolean&gt; currentTransactionReadOnly =\n        new NamedThreadLocal&lt;&gt;(\"Current transaction read-only status\");\n\nprivate static final ThreadLocal&lt;Integer&gt; currentTransactionIsolationLevel =\n        new NamedThreadLocal&lt;&gt;(\"Current transaction isolation level\");\n\nprivate static final ThreadLocal&lt;Boolean&gt; actualTransactionActive =\n        new NamedThreadLocal&lt;&gt;(\"Actual transaction active\");\n<\/code><\/pre>\n<h1>4. \u7ec7\u5165\u4e8b\u52a1<\/h1>\n<p>spring-boot-autoconfig \u6a21\u5757\u4f1a\u5bfc\u5165\uff1a<code>TransactionAutoConfiguration -&gt; EnableTransactionManagement -&gt; ProxyTransactionManagementConfiguration<\/code>\u3002<\/p>\n<p><code>ProxyTransactionManagementConfiguration<\/code>\u4f1a\u6ce8\u518c Bean \uff1a <code>TransactionInterceptor<\/code>\uff08\u8981\u5207\u5165\u7684\u903b\u8f91\uff09\u3001<code>AnnotationTransactionAttributeSource<\/code>\uff08\u5207\u5165\u70b9\uff09\u3001<code>BeanFactoryTransactionAttributeSourceAdvisor<\/code>\uff08\u5207\u9762\uff09\u3002<\/p>\n<pre><code>@Configuration\npublic class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {\n\n    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)\n    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {\n        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();\n        advisor.setTransactionAttributeSource(transactionAttributeSource());\n        advisor.setAdvice(transactionInterceptor());\n        advisor.setOrder(this.enableTx.&lt;Integer&gt;getNumber(\"order\"));\n        return advisor;\n    }\n\n    @Bean\n    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n    public TransactionAttributeSource transactionAttributeSource() {\n        return new AnnotationTransactionAttributeSource();\n    }\n\n    @Bean\n    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)\n    public TransactionInterceptor transactionInterceptor() {\n        TransactionInterceptor interceptor = new TransactionInterceptor();\n        interceptor.setTransactionAttributeSource(transactionAttributeSource());\n        if (this.txManager != null) {\n            interceptor.setTransactionManager(this.txManager);\n        }\n        return interceptor;\n    }\n}\n<\/code><\/pre>\n<p><code>AnnotationAwareAspectJAutoProxyCreator<\/code> \u5728\u521b\u5efa Bean \u7684\u4ee3\u7406\u5bf9\u8c61\u65f6\uff0c\u4f1a\u83b7\u53d6\u5230 <code>BeanFactoryTransactionAttributeSourceAdvisor<\/code> \u5224\u65ad\u8be5 Bean \u7684\u54ea\u4e9b\u65b9\u6cd5\u9700\u8981\u7ec7\u5165\u4e8b\u52a1\uff0c\u4ece\u800c\u628a <code>TransactionInterceptor<\/code> \u7ec7\u5165\u5230\u4ee3\u7406\u5bf9\u8c61\u3002<\/p>\n<h1>5. TransactionInterceptor \u4e8b\u52a1\u62e6\u622a\u5668<\/h1>\n<p><code>TransactionInterceptor<\/code> \u88ab AOP \u7ec7\u5165\u5230\u4ee3\u7406\u5bf9\u8c61\uff0c\u62e6\u622a\u5bf9\u4e8b\u52a1\u65b9\u6cd5\u7684\u8c03\u7528\uff0c\u7136\u540e\u8c03\u7528\u7236\u7c7b <code>TransactionAspectSupport.invokeWithinTransaction<\/code>\uff0c\u8be5\u65b9\u6cd5\u8c03\u7528 <code>TransactionManager<\/code> \u5b9e\u73b0\u7c7b\uff0c\u5728\u6267\u884c\u76ee\u6807\u65b9\u6cd5\u524d\u540e\u52a0\u5165 \u83b7\u53d6\u4e8b\u52a1\u3001\u63d0\u4ea4\u4e8b\u52a1\u6216\u56de\u6eda\u4e8b\u52a1\u7684\u63a7\u5236\u3002<\/p>\n<pre><code>\/\/ TransactionInterceptor \npublic Object invoke(MethodInvocation invocation) throws Throwable {\n    Class&lt;?&gt; targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);\n\n    \/\/ Adapt to TransactionAspectSupport's invokeWithinTransaction...\n    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);\n}\n\n\/\/ TransactionAspectSupport\nprotected Object invokeWithinTransaction(Method method, @Nullable Class&lt;?&gt; targetClass,\n        final InvocationCallback invocation) throws Throwable {\n    \/\/ \u83b7\u53d6\u7c7b\u548c\u65b9\u6cd5\u7684\u4e8b\u52a1\u5c5e\u6027\u3002\u5982\u679c\u4e0d\u662f\u4e8b\u52a1\u65b9\u6cd5\uff0c txAttr \u662f null \u3002\n    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);\n\n    \/\/ \u6839\u636e\u4e8b\u52a1\u5c5e\u6027\u83b7\u53d6\u4e8b\u52a1\u7ba1\u7406\u5668\n    final PlatformTransactionManager tm = determineTransactionManager(txAttr);\n    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);\n\n    \/\/ \u7701\u7565 ReactiveTransactionManager \u76f8\u5173\u7684\u3002\u3002\n\n    \/\/ \u57fa\u4e8e\u6570\u636e\u5e93\u7684 DataSourceTransactionManager \u662f PlatformTransactionManager \u7684\u5b50\u7c7b\n    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);\n    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);\n\n    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {\n        \/\/ \u6807\u51c6\u4e8b\u52a1 demarcation with getTransaction and commit\/rollback calls.\n        \/\/ \u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u4e8b\u52a1\n        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);\n\n        Object retVal;\n        try {\n            \/\/ \u8c03\u7528\u76ee\u6807\u65b9\u6cd5\n            retVal = invocation.proceedWithInvocation();\n        }\n        catch (Throwable ex) {\n            \/\/ \u5904\u7406\u5f02\u5e38\uff1a\u56de\u6eda\u4e8b\u52a1\u6216\u6b63\u5e38\u63d0\u4ea4\u4e8b\u52a1\n            completeTransactionAfterThrowing(txInfo, ex);\n            throw ex;\n        }\n        finally {\n            cleanupTransactionInfo(txInfo);\n        }\n\n        if (vavrPresent &amp;&amp; VavrDelegate.isVavrTry(retVal)) {\n            \/\/ Set rollback-only in case of Vavr failure matching our rollback rules...\n            TransactionStatus status = txInfo.getTransactionStatus();\n            if (status != null &amp;&amp; txAttr != null) {\n                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);\n            }\n        }\n\n        \/\/ \u6b63\u5e38\u63d0\u4ea4\u4e8b\u52a1\n        commitTransactionAfterReturning(txInfo);\n        return retVal;\n    }\n\n    \/\/ \u7701\u7565 CallbackPreferringPlatformTransactionManager \u76f8\u5173\u7684\n}\n\nprotected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {\n    if (txInfo != null &amp;&amp; txInfo.getTransactionStatus() != null) {\n        if (logger.isTraceEnabled()) {\n            logger.trace(\"Completing transaction for [\" + txInfo.getJoinpointIdentification() + \"]\");\n        }\n        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());\n    }\n}\n<\/code><\/pre>\n<h1>6. AbstractPlatformTransactionManager \u4e8b\u52a1\u63a7\u5236\u5b9e\u73b0\u7c7b<\/h1>\n<p><code>AbstractPlatformTransactionManager<\/code> \u5728\u83b7\u53d6\u4e8b\u52a1\u65f6\u5b9e\u73b0\u4e86 Spring \u4e8b\u52a1\u4f20\u64ad\u884c\u4e3a\uff08\u521b\u5efa\u65b0\u4e8b\u52a1\u3001\u521b\u5efa\u65b0\u4e8b\u52a1\u5e76\u6302\u8d77\u5f53\u524d\u4e8b\u52a1\uff09\u3002<\/p>\n<p>\u4e8b\u52a1\u63d0\u4ea4\u6216\u56de\u6eda\u524d\u540e\u56de\u8c03\u5df2\u6ce8\u518c\u7684 <code>TransactionSynchronization<\/code> \u7684\u76f8\u5173\u65b9\u6cd5\u3002<\/p>\n<h2>6.1 \u521b\u5efa\u4e8b\u52a1<\/h2>\n<pre><code>public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {\n    \/\/ \u8fd9\u91cc\u53ea\u662f\u521b\u5efa\u4e86\u4e00\u4e2a\u4e8b\u52a1\u5bf9\u8c61\uff0c\u5e76\u6ca1\u6709\u7ed1\u5b9a\u5230\u5e95\u5c42\u7684\u8d44\u6e90\uff0c\u6bd4\u5982 JDBC \u8fde\u63a5\u3002\n    Object transaction = doGetTransaction();\n\n    boolean debugEnabled = logger.isDebugEnabled();\n\n    if (definition == null) {\n        definition = new DefaultTransactionDefinition();\n    }\n\n    if (isExistingTransaction(transaction)) {\n        \/\/ \u5982\u679c\u5f53\u524d\u5df2\u5b58\u5728\u4e8b\u52a1\uff0c\u8981\u6839\u636e\u65b0\u7684\u4f20\u64ad\u884c\u4e3a\u6765\u51b3\u5b9a\u5982\u4f55\u5904\u7406\n        return handleExistingTransaction(definition, transaction, debugEnabled);\n    }\n\n    \/\/ \u5230\u8fd9\u91cc\u8bf4\u660e\u5f53\u524d\u6ca1\u6709\u4e8b\u52a1\u5b58\u5728\u3002\n\n    \/\/ \u65b0\u4e8b\u52a1\u8981\u6c42\u5f53\u524d\u5fc5\u987b\u5b58\u5728\u4e8b\u52a1\uff0c\u629b\u51fa\u5f02\u5e38\n    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {\n        throw new IllegalTransactionStateException(\n                \"No existing transaction found for transaction marked with propagation 'mandatory'\");\n    }\n    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||\n            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||\n            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {\n        \/\/ \u6302\u8d77\u5f53\u524d\u4e8b\u52a1\uff0cnull \u8868\u793a\u6ca1\u6709\u4e8b\u52a1\uff0c\u4f46\u53ef\u80fd\u5b58\u5728\u540c\u6b65\u94a9\u5b50\u3002\n        SuspendedResourcesHolder suspendedResources = suspend(null);\n\n        try {\n            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);\n            \/\/ \u521b\u5efa\u4e00\u4e2a\u4e8b\u52a1\u5bf9\u8c61\u72b6\u6001\u3002\n            DefaultTransactionStatus status = newTransactionStatus(\n                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);\n\n            \/\/ \u8fd9\u91cc\u4f1a\u83b7\u5f97\u771f\u5b9e\u7684\u6570\u636e\u5e93\u8fde\u63a5\n            \/\/ TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());\n            doBegin(transaction, definition);\n\n            \/\/ \u521d\u59cb\u5316 TransactionSynchronizationManager \u7684\u7ebf\u7a0b\u672c\u5730\u53d8\u91cf\uff0c\u66f4\u65b0\u4e3a\u5f53\u524d\u4e8b\u52a1\u7684\u3002\n            prepareSynchronization(status, definition);\n            return status;\n        }\n        catch (RuntimeException ex) {\n            resume(null, suspendedResources);\n            throw ex;\n        }\n        catch (Error err) {\n            resume(null, suspendedResources);\n            throw err;\n        }\n    }\n    else {\n        \/\/ \u521b\u5efa\u4e00\u4e2a\"\u7a7a\"\u4e8b\u52a1\uff1a\u975e\u771f\u5b9e\u7684\u4e8b\u52a1\uff0c\u4f46\u51c6\u5907\u4e86\u540c\u6b65\u94a9\u5b50\n        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);\n        return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);\n    }\n}\n\nprotected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {\n    if (status.isNewSynchronization()) {\n        TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());\n        TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(\n                definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?\n                        definition.getIsolationLevel() : null);\n        TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());\n        TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());\n        TransactionSynchronizationManager.initSynchronization();\n    }\n}\n\n\/\/ DataSourceTransactionManager\nprotected void doBegin(Object transaction, TransactionDefinition definition) {\n    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;\n    Connection con = null;\n\n    try {\n        if (txObject.getConnectionHolder() == null ||\n                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {\n            Connection newCon = this.dataSource.getConnection();\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Acquired Connection [\" + newCon + \"] for JDBC transaction\");\n            }\n            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);\n        }\n\n        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);\n        con = txObject.getConnectionHolder().getConnection();\n\n        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);\n        txObject.setPreviousIsolationLevel(previousIsolationLevel);\n\n        \/\/ Switch to manual commit if necessary. This is very expensive in some JDBC drivers,\n        \/\/ so we don't want to do it unnecessarily (for example if we've explicitly\n        \/\/ configured the connection pool to set it already).\n        if (con.getAutoCommit()) {\n            txObject.setMustRestoreAutoCommit(true);\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Switching JDBC Connection [\" + con + \"] to manual commit\");\n            }\n            con.setAutoCommit(false);\n        }\n        txObject.getConnectionHolder().setTransactionActive(true);\n\n        int timeout = determineTimeout(definition);\n        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {\n            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);\n        }\n\n        \/\/ Bind the session holder to the thread.\n        if (txObject.isNewConnectionHolder()) {\n            TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());\n        }\n    }\n\n    catch (Throwable ex) {\n        if (txObject.isNewConnectionHolder()) {\n            DataSourceUtils.releaseConnection(con, this.dataSource);\n            txObject.setConnectionHolder(null, false);\n        }\n        throw new CannotCreateTransactionException(\"Could not open JDBC Connection for transaction\", ex);\n    }\n}\n<\/code><\/pre>\n<h2>6.2 \u4e8b\u52a1\u63d0\u4ea4<\/h2>\n<p>\u4e8b\u52a1\u63d0\u4ea4\u5e76\u4e0d\u662f\u6bcf\u6b21\u8c03\u7528\u90fd\u63d0\u4ea4\u5e95\u5c42\u7684\u4e8b\u52a1\uff0c\u800c\u662f\u53ea\u6709\u521d\u59cb\u4e8b\u52a1\u624d\u4f1a\u63d0\u4ea4\u3002<\/p>\n<pre><code>private void processCommit(DefaultTransactionStatus status) throws TransactionException {\n    try {\n        boolean beforeCompletionInvoked = false;\n\n        try {\n            boolean unexpectedRollback = false;\n            prepareForCommit(status);\n            triggerBeforeCommit(status);\n            triggerBeforeCompletion(status);\n            beforeCompletionInvoked = true;\n\n            if (status.hasSavepoint()) {\n                if (status.isDebug()) {\n                    logger.debug(\"Releasing transaction savepoint\");\n                }\n                unexpectedRollback = status.isGlobalRollbackOnly();\n                status.releaseHeldSavepoint();\n            }\n            else if (status.isNewTransaction()) {\n                \/\/ \u662f\u521d\u59cb\u4e8b\u52a1\u624d\u4f1a\u63d0\u4ea4\n                unexpectedRollback = status.isGlobalRollbackOnly();\n                doCommit(status);\n            }\n            else if (isFailEarlyOnGlobalRollbackOnly()) {\n                unexpectedRollback = status.isGlobalRollbackOnly();\n            }\n\n            if (unexpectedRollback) {\n                throw new UnexpectedRollbackException(\n                        \"Transaction silently rolled back because it has been marked as rollback-only\");\n            }\n        }\n        catch (UnexpectedRollbackException ex) {\n            \/\/ can only be caused by doCommit\n            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);\n            throw ex;\n        }\n        catch (TransactionException ex) {\n            \/\/ can only be caused by doCommit\n            if (isRollbackOnCommitFailure()) {\n                doRollbackOnCommitException(status, ex);\n            }\n            else {\n                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);\n            }\n            throw ex;\n        }\n        catch (RuntimeException | Error ex) {\n            if (!beforeCompletionInvoked) {\n                triggerBeforeCompletion(status);\n            }\n            doRollbackOnCommitException(status, ex);\n            throw ex;\n        }\n\n        try {\n            triggerAfterCommit(status);\n        } finally {\n            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);\n        }\n\n    }\n    finally {\n        \/\/ \u8fd9\u4e2a\u4e8b\u52a1\u7ed3\u675f\u540e\uff0c\u8fdb\u884c\u6e05\u7406\uff0c\u7136\u540e\u6062\u590d\u88ab\u6302\u8d77\u7684\u4e8b\u52a1\n        cleanupAfterCompletion(status);\n    }\n}\n\nprivate void cleanupAfterCompletion(DefaultTransactionStatus status) {\n    status.setCompleted();\n    if (status.isNewSynchronization()) {\n        TransactionSynchronizationManager.clear();\n    }\n    if (status.isNewTransaction()) {\n        doCleanupAfterCompletion(status.getTransaction());\n    }\n    if (status.getSuspendedResources() != null) {\n        \/\/ \u6062\u590d\u88ab\u6302\u8d77\u7684\u4e8b\u52a1\n        Object transaction = (status.hasTransaction() ? status.getTransaction() : null);\n        resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());\n    }\n}\n<\/code><\/pre>\n<p><code>triggerXXX<\/code> \u65b9\u6cd5\u90fd\u662f\u56de\u8c03\u4e0e\u8fd9\u4e2a\u4e8b\u52a1\u76f8\u5173\u7684 <code>TransactionSynchronization.xXXX<\/code> \u65b9\u6cd5\u3002<\/p>\n<h1>7. MyBatis \u4e0e Spring \u4e8b\u52a1<\/h1>\n<p><code>MybatisAutoConfiguration<\/code> \u914d\u7f6e\u7c7b\u5b9a\u4e49\u4e86 Bean\uff1a<code>SqlSessionFactory<\/code> \u548c <code>SqlSessionTemplate<\/code>\u3002<\/p>\n<p>\u5728\u6784\u5efa <code>SqlSessionFactory<\/code> \u65f6\uff0c<code>SqlSessionFactoryBean.buildSqlSessionFactory<\/code> \u65b9\u6cd5\u91cc\u4f1a\u628a <code>transactionFactory<\/code> \u521d\u59cb\u5316\u4e3a <code>SpringManagedTransactionFactory<\/code> \u7684\u5b9e\u4f8b\uff0c\u5e76\u628a\u8fd9\u4e2a\u5b9e\u4f8b\u4f20\u7ed9 <code>Environment<\/code> \u3002<\/p>\n<p><code>@MapperScan<\/code> \u6ce8\u89e3\u4f1a\u5bfc\u5165 <code>MapperScannerRegistrar<\/code> \u6765\u626b\u63cf Mapper \u63a5\u53e3\u7c7b\uff0c\u5c01\u88c5\u6210 <code>MapperFactoryBean<\/code>\uff0c\u8be5 bean \u6ce8\u5165\u4e86 <code>SqlSessionTemplate<\/code> \u7528\u4e8e\u6267\u884c\u5404\u79cd\u6570\u636e\u5e93\u64cd\u4f5c\u3002<\/p>\n<p>MyBatis-Spring \u6a21\u5757\u8fd8\u4f1a\u628a\u83b7\u5f97\u7684 Session \u5c01\u88c5\u5728 SqlSessionHolder\uff0c\u4ee5 SqlSessionFactory \u4e3a\u952e\uff0c\u4ee5 SqlSessionHolder \u4e3a\u503c\u7f13\u5b58\u5728 TransactionSynchronizationManager \u7684 resource \u91cc\uff0c\u65b9\u4fbf\u5feb\u901f\u83b7\u53d6\u3002<\/p>\n<p>\u4e3a\u4e86\u4e0e Spring \u7684\u4e8b\u52a1\u52a8\u4f5c\u534f\u8c03\uff0c\u8fd8\u5411 TransactionSynchronizationManager \u6ce8\u518c\u4e86 SqlSessionSynchronization\uff0c\u4ee5\u4fbf\u5728 <code>suspend\/resume\/commit<\/code> \u7b49\u52a8\u4f5c\u524d\u540e\u5904\u7406 <code>SqlSessionHolder<\/code>\u3002<\/p>\n<blockquote><p>\n  \u6570\u636e\u5e93\u8fde\u63a5\u83b7\u53d6\u7684\u94fe\uff1a \u4ee3\u7406\u5bf9\u8c61 -> SqlSessionTemplate -> SqlSessionInterceptor -> SpringManagedTransaction -> DataSourceUtils -> Connection\n<\/p><\/blockquote>\n<h2>MyBatis \u4ee3\u7406\u5bf9\u8c61\u83b7\u53d6 SqlSession<\/h2>\n<p><code>SqlSessionInterceptor<\/code> \u8c03\u7528 <code>SqlSessionUtils.getSqlSession<\/code> \u65b9\u6cd5\u6765\u83b7\u53d6\u4e8b\u52a1\u3002<\/p>\n<pre><code>\/\/ SqlSessionUtils\npublic static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {\n    \/\/ \u5148\u4ece\u5f53\u524d\u4e8b\u52a1\u627e\n    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);\n\n    SqlSession session = sessionHolder(executorType, holder);\n    if (session != null) {\n        return session;\n    }\n\n    \/\/ \u5f53\u524d\u6ca1\u6709 SqlSession\uff0c\u521b\u5efa\u4e00\u4e2a\u3002\n    session = sessionFactory.openSession(executorType);\n\n    \/\/ \u6ce8\u518c\u65b0\u7684 SqlSession \u5230 TransactionSynchronizationManager\n    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);\n\n    return session;\n}\n\nprivate static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {\n    SqlSessionHolder holder;\n    if (TransactionSynchronizationManager.isSynchronizationActive()) {\n        Environment environment = sessionFactory.getConfiguration().getEnvironment();\n        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {\n            \/\/ \u628a\u5f53\u524d\u7684 SqlSessionHolder \u7ed1\u5b9a\u5230 resource\uff0c\u4e0e ConnectionHolder \u4e00\u81f4\u3002\n            holder = new SqlSessionHolder(session, executorType, exceptionTranslator);\n            TransactionSynchronizationManager.bindResource(sessionFactory, holder);\n\n            \/\/ \u6ce8\u518c\u4e8b\u52a1\u540c\u6b65\u94a9\u5b50\uff0c\u4ee5\u4fbf\u4e8e Spring \u4e8b\u52a1\u534f\u8c03\u3002\n            TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));\n            holder.setSynchronizedWithTransaction(true);\n            holder.requested();\n        } else {\n            if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {\n            } else {\n                throw new TransientDataAccessResourceException(\n                    \"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization\");\n            }\n        }\n    } else {    }\n}\n<\/code><\/pre>\n<h2>SpringManagedTransaction \u83b7\u53d6\u6570\u636e\u5e93\u8fde\u63a5<\/h2>\n<p>Executor \u6700\u7ec8\u8981\u6267\u884c\u6570\u636e\u5e93\u64cd\u4f5c\u65f6\uff0c\u5fc5\u987b\u8c03\u7528 <code>SqlSession.getConnection<\/code> \u65b9\u6cd5\u83b7\u53d6\u8fde\u63a5\uff0c\u4e5f\u5c31\u4f1a\u8c03\u7528\u5230 <code>SpringManagedTransaction.getConnection<\/code> \u65b9\u6cd5\uff0c\u5982\u4e0b\u3002<\/p>\n<pre><code class=\"java\">\/\/ SpringManagedTransaction\npublic Connection getConnection() throws SQLException {\n    if (this.connection == null) {\n        openConnection();\n    }\n    return this.connection;\n}\n\nprivate void openConnection() throws SQLException {\n    this.connection = DataSourceUtils.getConnection(this.dataSource);\n    this.autoCommit = this.connection.getAutoCommit();\n    this.isConnectionTransactional =   DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);\n}\n\n\n\/\/ DataSourceUtils\npublic static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {\n    try {\n        return doGetConnection(dataSource);\n    }\n    catch (SQLException ex) {\n        throw new CannotGetJdbcConnectionException(\"Could not get JDBC Connection\", ex);\n    }\n}\n\npublic static Connection doGetConnection(DataSource dataSource) throws SQLException {\n    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);\n    if (conHolder != null &amp;&amp; (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {\n        conHolder.requested();\n        if (!conHolder.hasConnection()) {\n            logger.debug(\"Fetching resumed JDBC Connection from DataSource\");\n            conHolder.setConnection(dataSource.getConnection());\n        }\n        return conHolder.getConnection();\n    }\n    \/\/ Else we either got no holder or an empty thread-bound holder here.\n\n    Connection con = dataSource.getConnection();\n\n    if (TransactionSynchronizationManager.isSynchronizationActive()) {\n        ConnectionHolder holderToUse = conHolder;\n        if (holderToUse == null) {\n            holderToUse = new ConnectionHolder(con);\n        }\n        else {\n            holderToUse.setConnection(con);\n        }\n        holderToUse.requested();\n        TransactionSynchronizationManager.registerSynchronization(\n                new ConnectionSynchronization(holderToUse, dataSource));\n        holderToUse.setSynchronizedWithTransaction(true);\n        if (holderToUse != conHolder) {\n            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);\n        }\n    }\n\n    return con;\n}\n<\/code><\/pre>\n<hr \/>\n<p>\u6b22\u8fce\u5173\u6ce8\u6211\u7684\u5fae\u4fe1\u516c\u4f17\u53f7: <strong>coderbee\u7b14\u8bb0<\/strong> \u3002<br \/>\n<img loading=\"lazy\" decoding=\"async\" width=\"258\" height=\"258\" src=\"https:\/\/coderbee.net\/wp-content\/uploads\/2019\/01\/coderbee-note.jpg\" class=\"alignnone size-full wp-image-1707\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. \u4e8b\u52a1\u7ba1\u7406\u5668\u62bd\u8c61 \u4e00\u4e2a\u4e8b\u52a1\u7ba1\u7406\u5668\u53ea\u9700\u8981\u4e09\u4e2a\u57fa\u672c\u7684\u80fd\u529b\uff1a\u83b7\u53d6\u4e00\u4e2a\u4e8b\u52a1\u3001\u63d0\u4ea4\u4e8b\u52a1 &hellip; <a href=\"https:\/\/coderbee.net\/index.php\/framework\/20210726\/2171\">\u7ee7\u7eed\u9605\u8bfb <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[255],"tags":[172,208],"_links":{"self":[{"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/posts\/2171"}],"collection":[{"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/comments?post=2171"}],"version-history":[{"count":2,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/posts\/2171\/revisions"}],"predecessor-version":[{"id":2173,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/posts\/2171\/revisions\/2173"}],"wp:attachment":[{"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/media?parent=2171"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/categories?post=2171"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderbee.net\/index.php\/wp-json\/wp\/v2\/tags?post=2171"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}