Это вложенности проблема, пожалуйста, помогите проанализировать причиныПочему получить соединение, которое было закрыто, в результате чего «держатель нулевой» ошибка
Общее описание кода структуры:
TransactionA в некоторых из DB, а затем откройте транзакцию B. TransactionA время фиксации, чтобы вызвать пользовательский спусковой крючок, курок в открытом transactionC (PROPAGATION_REQUIRES_NEW)
Процесс ошибки, как это: Первый запуск правильно не ошибка, второй раз бежать, чтобы добраться до закрытого соединения, код Test выглядит так: стек
@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...
}
});
}
}
Ошибка:
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]
Главный код:
//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 Подробный код:
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 Детальный код:
requiredTransactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
transactionActivityStore.addTransactionAction(actionRecord);
}
});
3 Код синхронизации (Проблема код):
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 Код синхронизации (Corre кт код):
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);
}
}
Ваша диаграмма неправильно. Круг не выполняется внутри транзакции A, он работает * после завершения транзакции A (она называется 'afterCompletion', правильно?). Таким образом, левый код пытается «обновить db-код», когда нет контекста транзакции, что подтверждается тем фактом, что «владелец подключения» пуст («null»). – Andreas
Большое спасибо. Я исправил фотографию. Процесс с ошибкой выглядит так: Первый запуск корректен без ошибок, во втором времени выполнения, чтобы перейти к закрытому соединению, я добавил тестовый код. Зато второе исполнение получит закрытое соединение. – sinory
Если вы не используете PROPAGATION_REQUIRES_NEW, то до того, как соединение не будет выпущено, просто закройте, что приведет к проблеме – sinory