java 事務解釋。


面試的時候,面試人員總喜歡問在spring中,

1. 如果一個主線程上有一個事務,在事務中開啟了一個線程。子線程跑出異常,對主線程有沒有影響,或者主線程產生異常對子線程有沒有影響。

這個時候,你只要記住主線程和子線程是不同的事務即可回答上面的問題,主線程跑出異常肯定對子線程沒有影響,但是子線程拋出異常對主線程有沒有影響,那就要看,拋出的異常是否為事務回滾有需要的異常類型,

如果是肯定會回滾,如果不是就不會回滾。

2.主線程為啥和子線程是不同的事務(在所有的事務傳播機制中)

因為 spring 中使用的ThreadLocal變量來保存 事務的執行者,entityManager。而ThreadLocal只能保證在當前線程中獲取entityManager。所以主子線程肯定是不同的事務。

        JpaTransactionObject txObject = new JpaTransactionObject();
        txObject.setSavepointAllowed(isNestedTransactionAllowed());
     //事務中的EntityManager 是從當前線程中獲取 即ThreadLocal
        EntityManagerHolder emHolder = (EntityManagerHolder)
                TransactionSynchronizationManager.getResource(obtainEntityManagerFactory());
        if (emHolder != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() +
                        "] for JPA transaction");
            }
            txObject.setEntityManagerHolder(emHolder, false);
        }

        if (getDataSource() != null) {
       //事務中connectionHolder也是從threadLocal中獲取。  ConnectionHolder conHolder
= (ConnectionHolder) TransactionSynchronizationManager.getResource(getDataSource()); txObject.setConnectionHolder(conHolder); }

 

3.事務的同步

  提到同步,首先想到的肯定是多線程。多線程肯定屬於不同的事務,事務的同步就是解決多線程之間事務的同步問題。

@Override
    @Transactional(propagation = Propagation.REQUIRED) public void saveRequire(Customer customer) throws Exception {  //1.保存客戶信息  repository.save(customer); exectorService.execute(()->{ //2.發送短信或郵件  });

這個例子是 1.保存客戶信息,2.發送短信或郵件--由於短信和郵件比較耗時,所以用異步進行操作。 要求:必須在保存客戶信息成功后,發送短信和郵件。

上面例子的問題是,發送短信和郵件,執行時,可能在事務完成后執行,也可能在事務完成之前執行。

事務的同步:就是事務完成后,同時執行發送短信和郵件。

因此上面的例子可以更改為

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveRequire(Customer customer) throws Exception { //1.保存客戶信息  repository.save(customer);
     //判斷當前事務是否激活 if (TransactionSynchronizationManager.isActualTransactionActive()) { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { exectorService.execute(()->{ //2.發送短信或郵件 }); } }); }else { exectorService.execute(()->{ //2.發送短信或郵件 }); }

4.事務中的主要對象介紹。

TransactionInfo 事務的對象。

    protected final class TransactionInfo {
     //事務管理器(獲取事務)
        @Nullable
        private final PlatformTransactionManager transactionManager;
        //事務屬性 (@Transactional(propagation = Propagation.REQUIRED)對應的這個注解的屬性)
        @Nullable
        private final TransactionAttribute transactionAttribute;
        //標記@Transactional的類和方法組成的字符串(com.zhou.test.transaction_test.service.impl.User1ServiceImpl.saveRequire) private final String joinpointIdentification;
     //當前事務的狀態(是否是新的事務,是否完成,是否有保存點,當前事務對象,當前被暫停的資源)
        @Nullable
        private TransactionStatus transactionStatus;

        @Nullable
     //被暫停的事務(例如require_new 需要一個新的事務,老的事務會被掛起)
private TransactionInfo oldTransactionInfo;

JpaTransactionObject 事務對象 (保存在 transactionStatus里面)

    private class JpaTransactionObject extends JdbcTransactionObjectSupport {
        //jpa 操作數據庫對象
        @Nullable
        private EntityManagerHolder entityManagerHolder;

        private boolean newEntityManagerHolder;
     //可能是jpaTransactionObject(事務暫停) 可能是SavepointManager(netesd,同一個事務回滾到保存點)
        @Nullable
        private Object transactionData;
JpaTransactionManager.SuspendedResourcesHolder 暫停的對象
    private static class SuspendedResourcesHolder {
     //數據源操作對象 private final EntityManagerHolder entityManagerHolder;
        //數據源操作對象
        @Nullable
        private final ConnectionHolder connectionHolder;

TransactionSynchronization  事務的同步類  實現下面的方法,可以在事務執行對應操作時,增加對應的處理。

default void suspend() {
    }

    
    default void resume() {
    }

    
    @Override
    default void flush() {
    }

    
    default void beforeCommit(boolean readOnly) {
    }

    
    default void beforeCompletion() {
    }

    
    default void afterCommit() {
    }

    
    default void afterCompletion(int status) {
    }

AbstractPlatformTransactionManager.SuspendedResourcesHolder 暫停的資源

    protected static class SuspendedResourcesHolder {
     //被暫停的對象,(存放 connectionHolder,entityManagerHoler)  
        @Nullable
        private final Object suspendedResources;      //存放當前事務同步的事件 @Nullable private List<TransactionSynchronization> suspendedSynchronizations;

 

DefaultTransactionStatus  事務狀態類。

public class DefaultTransactionStatus extends AbstractTransactionStatus {
  //當前事務對象(JPATransactionObject)。
    @Nullable
    private final Object transaction;

    private final boolean newTransaction;

    private final boolean newSynchronization;

    private final boolean readOnly;

    private final boolean debug;
    //暫停的資源(AbstractPlatformTransactionManager.SuspendedResourcesHolder)
    @Nullable
    private final Object suspendedResources;

5.nested 中的保存點

依賴的connection 中保存點實現。回滾時,回滾到保存點。

 6.事務的回滾原理

    protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.getTransactionStatus() != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
        //根據注釋transactionAttribute的rollbackFor屬性判斷此種異常是否回滾,如果找不到需要回滾的指定異常,就根據是否是父類中的RuntimeException和Error進行回滾
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } }
    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        try {
            boolean unexpectedRollback = unexpected;

            try {
                triggerBeforeCompletion(status);

                if (status.hasSavepoint()) {
                    if (status.isDebug()) {
                        logger.debug("Rolling back transaction to savepoint");
                    }
            //1.回滾的事務的保存點 status.rollbackToHeldSavepoint(); }
else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); }
//2.如果是新建的事務,直接回滾 doRollback(status); }
else { // Participating in larger transaction if (status.hasTransaction()) { if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); }
                 //3.如果用的已經存在的事務,則標記事務為回滾,等回到主事務中進行回滾 doSetRollbackOnly(status); }
else { if (status.isDebug()) { logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); } } } else { logger.debug("Should roll back transaction but cannot - no transaction available"); } // Unexpected rollback only matters here if we're asked to fail early if (!isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback = false; } } }

 

 

事務的處理過程 :TransactionInterceptor(只需關注事務攔截器即可。)

最后領着大家看下spring 的源碼,事務的處理原理:

1.先熟悉下 JPA事務   可以去hibernatenate官網下載實例:https://github.com/hibernate/hibernate-demos   

 

 

        EntityManager entityManager = openEntityManager();
        Session session = entityManager.unwrap(Session.class);
        session.beginTransaction();
        Tool tool = new Tool();
        tool.setName("Hammer111");
        session.save(tool);

        Future<?> future= exectorService.submit(()->{
            Tool tool1 = new Tool();
            tool1.setName("Hammer222");
            session.save(tool1);
            throw new RuntimeException();
        });
        future.get();
        
        session.getTransaction().commit();

 2. spring 中處理過程。

  主要查看 JpaTransactionManager.getTransaction(@Nullable TransactionDefinition definition)

  

    @Override
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
     //1.先獲取一個事務對象 Object transaction
= doGetTransaction();
    protected Object doGetTransaction() {
        JpaTransactionObject txObject = new JpaTransactionObject();
        txObject.setSavepointAllowed(isNestedTransactionAllowed());
     //2.獲取當前線程對應的EntityManagerHolder
        EntityManagerHolder emHolder = (EntityManagerHolder)
                TransactionSynchronizationManager.getResource(obtainEntityManagerFactory());
        if (emHolder != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Found thread-bound EntityManager [" + emHolder.getEntityManager() +
                        "] for JPA transaction");
            }
            txObject.setEntityManagerHolder(emHolder, false);
        }

        if (getDataSource() != null) {
            ConnectionHolder conHolder = (ConnectionHolder)
                    TransactionSynchronizationManager.getResource(getDataSource());
            txObject.setConnectionHolder(conHolder);
        }

        return txObject;
    }
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }
     //3.判斷當前是否有一個事務,並且事務是開啟的狀態 if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
         //4.如果當前沒有事務,就開啟一個事務,並且把這個事務綁定到當前線程 doBegin(transaction, definition); prepareSynchronization(status, definition);
return status; } catch (RuntimeException | Error ex) { resume(null, suspendedResources); throw ex; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + definition); } boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }

 

5.在一個代理類的內部方法互相調用是不會添加切面方法的。(這個時候,如果想在內部方法互相調用時加入代理該怎么辦?)

1.啟用暴露代理對象屬性

@EnableAspectJAutoProxy(exposeProxy=true)

2.內部互相調用時,寫法修改。

((Service) AopContext.currentProxy()).callMethodB();  

 6. JDBC事務

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM