Skip to content

Commit c1f1217

Browse files
ZPZP1zhangpeng
and
zhangpeng
authored
fix:事务后置回调中开启事务死循环 (#621)
Co-authored-by: zhangpeng <[email protected]>
1 parent 700d8c9 commit c1f1217

File tree

6 files changed

+68
-68
lines changed

6 files changed

+68
-68
lines changed

Diff for: dynamic-datasource-spring-boot-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/v1/DsTransactionalTest.java

-22
Original file line numberDiff line numberDiff line change
@@ -61,29 +61,13 @@ public void testDsTransactional() {
6161
PlaceOrderRequest placeOrderRequest = new PlaceOrderRequest(1, 1, 22, OrderStatus.INIT);
6262

6363
//商品不足
64-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
65-
@Override
66-
public void afterCompletion(int status) {
67-
if (status == STATUS_ROLLED_BACK) {
68-
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
69-
}
70-
}
71-
});
7264
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
7365
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
7466
assertThat(orderService.selectOrders()).isEmpty();
7567
assertThat(accountService.selectAccount()).isEqualTo(new Account(1, 50.0));
7668
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
7769

7870
//账户不足
79-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
80-
@Override
81-
public void afterCompletion(int status) {
82-
if (status == STATUS_ROLLED_BACK) {
83-
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
84-
}
85-
}
86-
});
8771
placeOrderRequest.setAmount(6);
8872
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
8973
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@@ -93,12 +77,6 @@ public void afterCompletion(int status) {
9377
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
9478

9579
//正常下单
96-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
97-
@Override
98-
public void afterCommit() {
99-
placeOrderRequest.setOrderStatus(OrderStatus.SUCCESS);
100-
}
101-
});
10280
placeOrderRequest.setAmount(5);
10381
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
10482
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(OrderStatus.INIT);

Diff for: dynamic-datasource-spring-boot-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/v1/service/tx/OrderService.java

+25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import com.baomidou.dynamic.datasource.annotation.DS;
1919
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
20+
import com.baomidou.dynamic.datasource.tx.TransactionContext;
2021
import org.springframework.stereotype.Service;
22+
import org.springframework.transaction.support.TransactionSynchronization;
2123

2224
import javax.sql.DataSource;
2325
import java.sql.*;
@@ -40,6 +42,29 @@ public OrderService(AccountService accountService, ProductService productService
4042

4143
@DSTransactional
4244
public int placeOrder(PlaceOrderRequest request) {
45+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
46+
@Override
47+
public void afterCompletion(int status) {
48+
if (status == STATUS_ROLLED_BACK) {
49+
request.setOrderStatus(OrderStatus.FAIL);
50+
}
51+
}
52+
});
53+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
54+
@Override
55+
public void afterCompletion(int status) {
56+
if (status == STATUS_ROLLED_BACK) {
57+
request.setOrderStatus(OrderStatus.FAIL);
58+
}
59+
}
60+
});
61+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
62+
@Override
63+
public void afterCommit() {
64+
request.setOrderStatus(OrderStatus.SUCCESS);
65+
}
66+
});
67+
4368
try (Connection connection = dataSource.getConnection()) {
4469
Integer userId = request.getUserId();
4570
Integer productId = request.getProductId();

Diff for: dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/v3/DsTransactionalTest.java

-22
Original file line numberDiff line numberDiff line change
@@ -61,29 +61,13 @@ public void testDsTransactional() {
6161
PlaceOrderRequest placeOrderRequest = new PlaceOrderRequest(1, 1, 22, OrderStatus.INIT);
6262

6363
//商品不足
64-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
65-
@Override
66-
public void afterCompletion(int status) {
67-
if (status == STATUS_ROLLED_BACK) {
68-
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
69-
}
70-
}
71-
});
7264
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
7365
assertThat(placeOrderRequest.getOrderStatus()).isEqualTo(OrderStatus.FAIL);
7466
assertThat(orderService.selectOrders()).isEmpty();
7567
assertThat(accountService.selectAccount()).isEqualTo(new Account(1, 50.0));
7668
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
7769

7870
//账户不足
79-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
80-
@Override
81-
public void afterCompletion(int status) {
82-
if (status == STATUS_ROLLED_BACK) {
83-
placeOrderRequest.setOrderStatus(OrderStatus.FAIL);
84-
}
85-
}
86-
});
8771
placeOrderRequest.setAmount(6);
8872
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
8973
assertThrows(RuntimeException.class, () -> orderService.placeOrder(placeOrderRequest));
@@ -93,12 +77,6 @@ public void afterCompletion(int status) {
9377
assertThat(productService.selectProduct()).isEqualTo(new Product(1, 10.0, 20));
9478

9579
//正常下单
96-
TransactionContext.registerSynchronization(new TransactionSynchronization() {
97-
@Override
98-
public void afterCommit() {
99-
placeOrderRequest.setOrderStatus(OrderStatus.SUCCESS);
100-
}
101-
});
10280
placeOrderRequest.setAmount(5);
10381
placeOrderRequest.setOrderStatus(OrderStatus.INIT);
10482
assertThat(orderService.placeOrder(placeOrderRequest)).isEqualTo(OrderStatus.INIT);

Diff for: dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/v3/service/tx/OrderService.java

+25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import com.baomidou.dynamic.datasource.annotation.DS;
1919
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
20+
import com.baomidou.dynamic.datasource.tx.TransactionContext;
2021
import org.springframework.stereotype.Service;
22+
import org.springframework.transaction.support.TransactionSynchronization;
2123

2224
import javax.sql.DataSource;
2325
import java.sql.*;
@@ -40,6 +42,29 @@ public OrderService(AccountService accountService, ProductService productService
4042

4143
@DSTransactional
4244
public int placeOrder(PlaceOrderRequest request) {
45+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
46+
@Override
47+
public void afterCompletion(int status) {
48+
if (status == STATUS_ROLLED_BACK) {
49+
request.setOrderStatus(OrderStatus.FAIL);
50+
}
51+
}
52+
});
53+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
54+
@Override
55+
public void afterCompletion(int status) {
56+
if (status == STATUS_ROLLED_BACK) {
57+
request.setOrderStatus(OrderStatus.FAIL);
58+
}
59+
}
60+
});
61+
TransactionContext.registerSynchronization(new TransactionSynchronization() {
62+
@Override
63+
public void afterCommit() {
64+
request.setOrderStatus(OrderStatus.SUCCESS);
65+
}
66+
});
67+
4368
try (Connection connection = dataSource.getConnection()) {
4469
Integer userId = request.getUserId();
4570
Integer productId = request.getProductId();

Diff for: dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/tx/TransactionContext.java

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public static void registerSynchronization(TransactionSynchronization synchroniz
8181
if (Objects.isNull(synchronization)) {
8282
throw new IllegalArgumentException("TransactionSynchronization must not be null");
8383
}
84+
if (DsStrUtils.isEmpty(TransactionContext.getXID())) {
85+
throw new IllegalStateException("Transaction is not active");
86+
}
8487
Set<TransactionSynchronization> synchs = SYNCHRONIZATION_HOLDER.get();
8588
synchs.add(synchronization);
8689
}

Diff for: dynamic-datasource-spring/src/main/java/com/baomidou/dynamic/datasource/tx/TransactionalTemplate.java

+15-24
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,22 @@ private Object doExecute(TransactionalExecutor transactionalExecutor) throws Thr
113113
boolean state = true;
114114
Object o;
115115
String xid = LocalTxUtil.startTransaction();
116+
boolean shouldInvokeAction = TransactionContext.getSynchronizations().isEmpty();
116117
try {
117118
o = transactionalExecutor.execute();
118119
} catch (Exception e) {
119120
state = !isRollback(e, transactionInfo);
120121
throw e;
121122
} finally {
122-
invokeBeforeCompletion();
123+
invokeBeforeCompletion(shouldInvokeAction);
123124
if (state) {
124-
invokeBeforeCommit();
125+
invokeBeforeCommit(shouldInvokeAction);
125126
LocalTxUtil.commit(xid);
126-
invokeAfterCommit();
127-
invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED);
127+
invokeAfterCommit(shouldInvokeAction);
128+
invokeAfterCompletion(TransactionSynchronization.STATUS_COMMITTED, shouldInvokeAction);
128129
} else {
129130
LocalTxUtil.rollback(xid);
130-
invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
131+
invokeAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK, shouldInvokeAction);
131132
}
132133
}
133134
return o;
@@ -241,8 +242,8 @@ public boolean isNotEmpty(Object[] array) {
241242
/**
242243
* Invoke before commit.
243244
*/
244-
public void invokeBeforeCommit() {
245-
if (shouldInvokeAction()) {
245+
public void invokeBeforeCommit(boolean shouldInvokeAction) {
246+
if (shouldInvokeAction) {
246247
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
247248
synchronization.beforeCommit(false);
248249
}
@@ -252,8 +253,8 @@ public void invokeBeforeCommit() {
252253
/**
253254
* Invoke before completion .
254255
*/
255-
public void invokeBeforeCompletion() {
256-
if (shouldInvokeAction()) {
256+
public void invokeBeforeCompletion(boolean shouldInvokeAction) {
257+
if (shouldInvokeAction) {
257258
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
258259
synchronization.beforeCompletion();
259260
}
@@ -263,8 +264,8 @@ public void invokeBeforeCompletion() {
263264
/**
264265
* Invoke after commit.
265266
*/
266-
public void invokeAfterCommit() {
267-
if (shouldInvokeAction()) {
267+
public void invokeAfterCommit(boolean shouldInvokeAction) {
268+
if (shouldInvokeAction) {
268269
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
269270
synchronization.afterCommit();
270271
}
@@ -274,22 +275,12 @@ public void invokeAfterCommit() {
274275
/**
275276
* Invoke after completion.
276277
*/
277-
public void invokeAfterCompletion(int status) {
278-
if (shouldInvokeAction()) {
278+
public void invokeAfterCompletion(int status, boolean shouldInvokeAction) {
279+
if (shouldInvokeAction) {
279280
for (TransactionSynchronization synchronization : TransactionContext.getSynchronizations()) {
280281
synchronization.afterCompletion(status);
281282
}
283+
TransactionContext.removeSynchronizations();
282284
}
283-
TransactionContext.removeSynchronizations();
284-
}
285-
286-
/**
287-
* Should invoke action boolean.
288-
*
289-
* @return the boolean
290-
*/
291-
public boolean shouldInvokeAction() {
292-
//If there is a savepoint, the action should not be executed
293-
return !ConnectionFactory.hasSavepoint(TransactionContext.getXID());
294285
}
295286
}

0 commit comments

Comments
 (0)