fix:事务后置回调中开启事务死循环 (#621)
Co-authored-by: zhangpeng <xinniankuailezp@163.com>
This commit is contained in:
parent
700d8c904c
commit
c1f12172e2
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user