2016-09-22 1 views
-1

Ceci est le problème d'imbrication, s'il vous plaît aider à analyser les raisonsPourquoi obtenir la connexion qui a été fermée, ce qui « porte est nulle » erreur

Description de la structure du code général:

TransactionA dans certains des Opération DB, puis ouvrez la transactionB. TransactionA consacrer du temps pour déclencher un déclencheur personnalisé, la détente en plein air transactionC (PROPAGATION_REQUIRES_NEW)

enter image description here

Le processus d'erreur est comme ceci: La première course est correcte aucune erreur, la deuxième fois de course pour arriver à une connexion fermée, le code de test est comme suit:

@Test 
public void testNotify() { 

    ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor(); 

    while (true) { 

     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     threadPoolExecutor.execute(new Runnable() { 
      @Override 
      public void run() { 

       //The following mainCode... 

      } 
     }); 
    } 

} 

pile d'erreur:

Caused by: java.sql.SQLException: connection holder is null 
    at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1122) ~[druid-1.0.24.jar:1.0.24] 
    at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1113) ~[druid-1.0.24.jar:1.0.24] 
    at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:318) ~[druid-1.0.24.jar:1.0.24] 
    at org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement(PreparedStatementHandler.java:75) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.statement.BaseStatementHandler.prepare(BaseStatementHandler.java:85) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare(RoutingStatementHandler.java:57) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:73) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77) ~[mybatis-3.2.8.jar:3.2.8] 
    at sun.reflect.GeneratedMethodAccessor132.invoke(Unknown Source) ~[na:na] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45] 
    at org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:49) ~[mybatis-3.2.8.jar:3.2.8] 
    at com.youzan.pay.assetcenter.dal.monitor.SqlMonitorManager.intercept(SqlMonitorManager.java:53) ~[assetcenter.dal-0.0.1-SNAPSHOT.jar:na] 
    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:60) ~[mybatis-3.2.8.jar:3.2.8] 
    at com.sun.proxy.$Proxy60.query(Unknown Source) ~[na:na] 
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102) ~[mybatis-3.2.8.jar:3.2.8] 
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:66) ~[mybatis-3.2.8.jar:3.2.8] 
    at sun.reflect.GeneratedMethodAccessor140.invoke(Unknown Source) ~[na:na] 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45] 
    at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45] 
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ~[mybatis-spring-1.2.2.jar:1.2.2] 

Code principal:

//Open a local transaction PROPAGATION_REQUIRED 
    transactionTemplate.execute(new TransactionCallback<Boolean>() { 

      @Override 
      public Boolean doInTransaction(TransactionStatus status) { 

       AcquireOrder acquireOrder = acquireOrderRepository.active(payResult.getAcquireNo()); 

       // Do some set... 
        acquireOrderRepository.reStore(acquireOrder); 
       } 

       //1. Look at the following 
        transactionActivityService.start("assetcenter", "", new HashMap<>()); 

        //... Some of the code to construct command 

        //2.Look at the following 
        transactionActivityService.enrollAction(SETTLEMENT_TOPIC, 
         JSONObject.toJSONString(settleCommand)); 

        //3.Look at the following 
        transactionActivityService.enrollAction(CHARGE_TOPIC, JSONObject.toJSONString(payCommand)); 
       } 

       return Boolean.TRUE; 
      } 
     }); 

1 Code détaillée:

try { 
      TransactionActivityRecord activity = requiredTransactionTemplate.execute(new TransactionCallbackWithoutResult() { 
      @Override 
      protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { 
       transactionActivityStore.addTransactionActivity(activityRecord); 
      } 
     }); 
      TransactionActivityContextHolder.setCurrent(activity); 
     } catch (RuntimeException e) { 
      TransactionActivityContextHolder.clear(); 
      throw e; 
     } finally { 
      if (TransactionActivityContextHolder.isActive()) { 

       //The registration of synchronizer 
       TransactionSynchronization synchronization = new FinalizeTransactionSynchronization(transactionActivityManager); 
       TransactionSynchronizationManager.registerSynchronization(synchronization); 
      } 
     } 

2,3 Code détaillée:

requiredTransactionTemplate.execute(new TransactionCallbackWithoutResult() { 
      @Override 
      protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { 
       transactionActivityStore.addTransactionAction(actionRecord); 
      } 
     }); 

3 Le code de synchronisation (code de problème):

public void afterCompletion(int status) { 

     boolean actionRes = doSubmitActions(actions); 

//This code raises questions. 
     if(actionRes && activity.getState() == TransactionState.INIT) { 
      transactionActivityStore.updateTransactionActivityState(activity.getTxId(),TransactionState.PREPARE); 
     } 

    } 


/** 
    * 执行事务参与者的提交 
    * @param actions 
    * @return 
    */ 
    private boolean doSubmitActions(List<TransactionActionRecord> actions) { 
     AtomicBoolean result = new AtomicBoolean(true); 
     if(actions != null && !actions.isEmpty()) { 
      actions.stream().filter(action -> action.getState() == TransactionState.INIT).forEach(action -> { 
       boolean res = dtsTransactionActionProducer 
         .push(action.getActionName(), JSON.toJSONString(action.getContext())); 
       if (res) { 
//Open a local transaction PROPAGATION_REQUIRES_NEW 
        requiresNewTransactionTemplate.execute(new TransactionCallbackWithoutResult() { 

         @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { 
          transactionActivityStore 
            .updateTransactionActionState(action.getActionId(), TransactionState.PREPARE); 
         } 
        }); 
       } else { 
        log.warn("submit transaction-action failure topic:{},context:{}", action.getActionName(), 
          action.getContext()); 
        result.compareAndSet(true,false); 
       } 
      }); 
     } 
     return result.get(); 
    } 

3 Le code de synchronisation (Corre Code ct):

public void afterCompletion(int status) { 

     boolean actionRes = doSubmitActions(actions); 

//Put the following code in the doSubmitActions method in the requiresNewTransactionTemplate execution 
     if(actionRes && activity.getState() == TransactionState.INIT) { 
      transactionActivityStore.updateTransactionActivityState(activity.getTxId(),TransactionState.PREPARE); 
     } 

    } 
+0

Votre diagramme est erroné. Le cercle n'est pas exécuté dans la transaction A, il est en cours d'exécution * après * la transaction A est terminée (elle s'appelle 'afterCompletion', non?). Le code de gauche essaie donc de "mettre à jour le code db" lorsqu'il n'y a pas de contexte de transaction, ce qui est illustré par le fait que le "détenteur de la connexion" est vide ("null"). – Andreas

+0

Merci beaucoup. J'ai corrigé l'image. Le processus d'erreur est comme ceci: La première exécution est correcte aucune erreur, la deuxième exécution pour obtenir une connexion fermée, j'ai ajouté le code de test. Pourquoi la deuxième exécution obtiendra une connexion qui a été fermée. – sinory

+0

Si vous n'utilisez pas la PROPAGATION_REQUIRES_NEW avant la connexion ne peut pas être libérée, il suffit de fermer, ce qui entraîne le problème – sinory

Répondre

0

Si vous n'utilisez pas le PROPAGATION_REQUIRES_NEW sera avant que la connexion ne peut être libéré, juste être fermé, entraînant le problème

/** 
    * Invoked after transaction commit. Can perform further operations right 
    * <i>after</i> the main transaction has <i>successfully</i> committed. 
    * <p>Can e.g. commit further operations that are supposed to follow on a successful 
    * commit of the main transaction, like confirmation messages or emails. 
    * <p><b>NOTE:</b> The transaction will have been committed already, but the 
    * transactional resources might still be active and accessible. As a consequence, 
    * any data access code triggered at this point will still "participate" in the 
    * original transaction, allowing to perform some cleanup (with no commit following 
    * anymore!), unless it explicitly declares that it needs to run in a separate 
    * transaction. Hence: <b>Use {@code PROPAGATION_REQUIRES_NEW} for any 
    * transactional operation that is called from here.</b> 
    * @throws RuntimeException in case of errors; will be <b>propagated to the caller</b> 
    * (note: do not throw TransactionException subclasses here!) 
    */ 
    void afterCommit();