fix:事务后置回调中开启事务死循环 (#621)

Co-authored-by: zhangpeng <xinniankuailezp@163.com>
This commit is contained in:
Z.P 2024-01-17 10:23:25 +08:00 committed by GitHub
parent 700d8c904c
commit c1f12172e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 68 additions and 68 deletions

View File

@ -61,14 +61,6 @@ public class DsTransactionalTest {
PlaceOrderRequest placeOrderRequest = new PlaceOrderRequest(1, 1, 22, OrderStatus.INIT);
//商品不足
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
}
}
});
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
assertThat(orderService.selectOrders()).isEmpty();
@ -76,14 +68,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
//账户不足
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
}
}
});
placeOrderRequest.setAmount(6);
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@ -93,12 +77,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
//正常下单
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
placeOrderRequest.setOrderStatus(OrderStatus.SUCCESS);
}
});
placeOrderRequest.setAmount(5);
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(OrderStatus.INIT);

View File

@ -17,7 +17,9 @@ package com.baomidou.dynamic.datasource.fixture.v1.service.tx;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.TransactionContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import javax.sql.DataSource;
import java.sql.*;
@ -40,6 +42,29 @@ public class OrderService {
@DSTransactional
public int placeOrder(PlaceOrderRequest request) {
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
request.setOrderStatus(OrderStatus.FAIL);
}
}
});
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
request.setOrderStatus(OrderStatus.FAIL);
}
}
});
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
request.setOrderStatus(OrderStatus.SUCCESS);
}
});
try (Connection connection = dataSource.getConnection()) {
Integer userId = request.getUserId();
Integer productId = request.getProductId();

View File

@ -61,14 +61,6 @@ public class DsTransactionalTest {
PlaceOrderRequest placeOrderRequest = new PlaceOrderRequest(1, 1, 22, OrderStatus.INIT);
//商品不足
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
}
}
});
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
assertThat(orderService.selectOrders()).isEmpty();
@ -76,14 +68,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
//账户不足
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
}
}
});
placeOrderRequest.setAmount(6);
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@ -93,12 +77,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
//正常下单
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
placeOrderRequest.setOrderStatus(OrderStatus.SUCCESS);
}
});
placeOrderRequest.setAmount(5);
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(OrderStatus.INIT);

View File

@ -17,7 +17,9 @@ package com.baomidou.dynamic.datasource.fixture.v3.service.tx;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.TransactionContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import javax.sql.DataSource;
import java.sql.*;
@ -40,6 +42,29 @@ public class OrderService {
@DSTransactional
public int placeOrder(PlaceOrderRequest request) {
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
request.setOrderStatus(OrderStatus.FAIL);
}
}
});
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
if (status == STATUS_ROLLED_BACK) {
request.setOrderStatus(OrderStatus.FAIL);
}
}
});
TransactionContext.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
request.setOrderStatus(OrderStatus.SUCCESS);
}
});
try (Connection connection = dataSource.getConnection()) {
Integer userId = request.getUserId();
Integer productId = request.getProductId();

View File

@ -81,6 +81,9 @@ public class TransactionContext {
if (Objects.isNull(synchronization)) {
throw new IllegalArgumentException("TransactionSynchronization must not be null");
}
if (DsStrUtils.isEmpty(TransactionContext.getXID())) {
throw new IllegalStateException("Transaction is not active");
}
Set<TransactionSynchronization> synchs = SYNCHRONIZATION_HOLDER.get();
synchs.add(synchronization);
}

View File

@ -113,21 +113,22 @@ public class TransactionalTemplate {
boolean state = true;
Object o;
String xid = LocalTxUtil.startTransaction();
boolean shouldInvokeAction = TransactionContext.getSynchronizations().isEmpty();
try {
o = transactionalExecutor.execute();
} catch (Exception e) {
state = !isRollback(e, transactionInfo);
throw e;
} finally {
invokeBeforeCompletion();
invokeBeforeCompletion(shouldInvokeAction);
if (state) {
invokeBeforeCommit();
invokeBeforeCommit(shouldInvokeAction);
LocalTxUtil.commit(xid);
invokeAfterCommit();
invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED);
invokeAfterCommit(shouldInvokeAction);
invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED, shouldInvokeAction);
} else {
LocalTxUtil.rollback(xid);
invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK, shouldInvokeAction);
}
}
return o;
@ -241,8 +242,8 @@ public class TransactionalTemplate {
/**
* Invoke before commit.
*/
public void invokeBeforeCommit() {
if (shouldInvokeAction()) {
public void invokeBeforeCommit(boolean shouldInvokeAction) {
if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.beforeCommit(false);
}
@ -252,8 +253,8 @@ public class TransactionalTemplate {
/**
* Invoke before completion .
*/
public void invokeBeforeCompletion() {
if (shouldInvokeAction()) {
public void invokeBeforeCompletion(boolean shouldInvokeAction) {
if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.beforeCompletion();
}
@ -263,8 +264,8 @@ public class TransactionalTemplate {
/**
* Invoke after commit.
*/
public void invokeAfterCommit() {
if (shouldInvokeAction()) {
public void invokeAfterCommit(boolean shouldInvokeAction) {
if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.afterCommit();
}
@ -274,22 +275,12 @@ public class TransactionalTemplate {
/**
* Invoke after completion.
*/
public void invokeAfterCompletion(int status) {
if (shouldInvokeAction()) {
public void invokeAfterCompletion(int status, boolean shouldInvokeAction) {
if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.afterCompletion(status);
}
TransactionContext.removeSynchronizations();
}
TransactionContext.removeSynchronizations();
}
/**
* Should invoke action boolean.
*
* @return the boolean
*/
public boolean shouldInvokeAction() {
//If there is a savepoint, the action should not be executed
return !ConnectionFactory.hasSavepoint(TransactionContext.getXID());
}
}