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); 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)); assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL); assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
assertThat(orderService.selectOrders()).isEmpty(); assertThat(orderService.selectOrders()).isEmpty();
@ -76,14 +68,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20)); 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.setAmount(6);
placeOrderRequest.setOrderStatus(OrderStatus.INIT); placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest)); assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@ -93,12 +77,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20)); 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.setAmount(5);
placeOrderRequest.setOrderStatus(OrderStatus.INIT); placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(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.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.TransactionContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.*; import java.sql.*;
@ -40,6 +42,29 @@ public class OrderService {
@DSTransactional @DSTransactional
public int placeOrder(PlaceOrderRequest request) { 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()) { try (Connection connection = dataSource.getConnection()) {
Integer userId = request.getUserId(); Integer userId = request.getUserId();
Integer productId = request.getProductId(); Integer productId = request.getProductId();

View File

@ -61,14 +61,6 @@ public class DsTransactionalTest {
PlaceOrderRequest placeOrderRequest = new PlaceOrderRequest(1, 1, 22, OrderStatus.INIT); 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)); assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL); assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
assertThat(orderService.selectOrders()).isEmpty(); assertThat(orderService.selectOrders()).isEmpty();
@ -76,14 +68,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20)); 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.setAmount(6);
placeOrderRequest.setOrderStatus(OrderStatus.INIT); placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest)); assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@ -93,12 +77,6 @@ public class DsTransactionalTest {
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20)); 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.setAmount(5);
placeOrderRequest.setOrderStatus(OrderStatus.INIT); placeOrderRequest.setOrderStatus(OrderStatus.INIT);
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(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.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.tx.TransactionContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.*; import java.sql.*;
@ -40,6 +42,29 @@ public class OrderService {
@DSTransactional @DSTransactional
public int placeOrder(PlaceOrderRequest request) { 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()) { try (Connection connection = dataSource.getConnection()) {
Integer userId = request.getUserId(); Integer userId = request.getUserId();
Integer productId = request.getProductId(); Integer productId = request.getProductId();

View File

@ -81,6 +81,9 @@ public class TransactionContext {
if (Objects.isNull(synchronization)) { if (Objects.isNull(synchronization)) {
throw new IllegalArgumentException("TransactionSynchronization must not be null"); 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(); Set<TransactionSynchronization> synchs = SYNCHRONIZATION_HOLDER.get();
synchs.add(synchronization); synchs.add(synchronization);
} }

View File

@ -113,21 +113,22 @@ public class TransactionalTemplate {
boolean state = true; boolean state = true;
Object o; Object o;
String xid = LocalTxUtil.startTransaction(); String xid = LocalTxUtil.startTransaction();
boolean shouldInvokeAction = TransactionContext.getSynchronizations().isEmpty();
try { try {
o = transactionalExecutor.execute(); o = transactionalExecutor.execute();
} catch (Exception e) { } catch (Exception e) {
state = !isRollback(e, transactionInfo); state = !isRollback(e, transactionInfo);
throw e; throw e;
} finally { } finally {
invokeBeforeCompletion(); invokeBeforeCompletion(shouldInvokeAction);
if (state) { if (state) {
invokeBeforeCommit(); invokeBeforeCommit(shouldInvokeAction);
LocalTxUtil.commit(xid); LocalTxUtil.commit(xid);
invokeAfterCommit(); invokeAfterCommit(shouldInvokeAction);
invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED); invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED, shouldInvokeAction);
} else { } else {
LocalTxUtil.rollback(xid); LocalTxUtil.rollback(xid);
invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK); invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK, shouldInvokeAction);
} }
} }
return o; return o;
@ -241,8 +242,8 @@ public class TransactionalTemplate {
/** /**
* Invoke before commit. * Invoke before commit.
*/ */
public void invokeBeforeCommit() { public void invokeBeforeCommit(boolean shouldInvokeAction) {
if (shouldInvokeAction()) { if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) { for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.beforeCommit(false); synchronization.beforeCommit(false);
} }
@ -252,8 +253,8 @@ public class TransactionalTemplate {
/** /**
* Invoke before completion . * Invoke before completion .
*/ */
public void invokeBeforeCompletion() { public void invokeBeforeCompletion(boolean shouldInvokeAction) {
if (shouldInvokeAction()) { if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) { for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.beforeCompletion(); synchronization.beforeCompletion();
} }
@ -263,8 +264,8 @@ public class TransactionalTemplate {
/** /**
* Invoke after commit. * Invoke after commit.
*/ */
public void invokeAfterCommit() { public void invokeAfterCommit(boolean shouldInvokeAction) {
if (shouldInvokeAction()) { if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) { for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.afterCommit(); synchronization.afterCommit();
} }
@ -274,22 +275,12 @@ public class TransactionalTemplate {
/** /**
* Invoke after completion. * Invoke after completion.
*/ */
public void invokeAfterCompletion(int status) { public void invokeAfterCompletion(int status, boolean shouldInvokeAction) {
if (shouldInvokeAction()) { if (shouldInvokeAction) {
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) { for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
synchronization.afterCompletion(status); 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());
} }
} }