mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
Merge pull request #424 from Aliothmoon/fix/issue-418
fix: 修复事务超时时间问题 & 提供更完善的Spring事务定义上下文
This commit is contained in:
commit
657cb59b79
@ -176,7 +176,8 @@ public class FlexDataSource extends AbstractDataSource {
|
||||
public Connection proxy(Connection connection, String xid) {
|
||||
return (Connection) Proxy.newProxyInstance(FlexDataSource.class.getClassLoader()
|
||||
, new Class[]{Connection.class}
|
||||
, new ConnectionHandler(connection, xid));
|
||||
, new ConnectionHandler(connection, xid)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,6 +19,7 @@ import com.mybatisflex.core.datasource.FlexDataSource;
|
||||
import com.mybatisflex.core.transaction.TransactionContext;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
@ -82,6 +83,10 @@ public class FlexSpringTransaction implements Transaction {
|
||||
|
||||
@Override
|
||||
public Integer getTimeout() throws SQLException {
|
||||
TransactionDefinition definition = TransactionDefinitionManager.getTransactionDefinition();
|
||||
if (definition != null) {
|
||||
return TransactionDefinitionManager.getTimeToLiveInSeconds();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
*/
|
||||
public class FlexTransactionManager extends AbstractPlatformTransactionManager {
|
||||
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() throws TransactionException {
|
||||
return new TransactionObject(TransactionContext.getXID());
|
||||
@ -58,6 +59,7 @@ public class FlexTransactionManager extends AbstractPlatformTransactionManager {
|
||||
@Override
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
|
||||
TransactionObject transactionObject = (TransactionObject) transaction;
|
||||
TransactionDefinitionManager.setTransactionDefinition(definition);
|
||||
transactionObject.currentXid = TransactionalManager.startTransactional();
|
||||
}
|
||||
|
||||
@ -84,9 +86,14 @@ public class FlexTransactionManager extends AbstractPlatformTransactionManager {
|
||||
transactionObject.setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
TransactionDefinitionManager.clear();
|
||||
}
|
||||
|
||||
static class TransactionObject extends JdbcTransactionObjectSupport {
|
||||
|
||||
|
||||
private static final ThreadLocal<String> ROLLBACK_ONLY_XIDS = new ThreadLocal<>();
|
||||
|
||||
private final String prevXid;
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
package com.mybatisflex.spring;
|
||||
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionTimedOutException;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 事务定义管理器 用于更完整的实现Spring事务
|
||||
* 仅支持传统事务不支持R2DBC事务
|
||||
*
|
||||
* @author Aliothmoon
|
||||
* @since 2024/10/25
|
||||
*/
|
||||
public final class TransactionDefinitionManager {
|
||||
private static final ThreadLocal<TransactionDefinition> TRANSACTION_DEFINITION = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Date> TRANSACTION_DEADLINE = new ThreadLocal<>();
|
||||
|
||||
public static TransactionDefinition getTransactionDefinition() {
|
||||
return TRANSACTION_DEFINITION.get();
|
||||
}
|
||||
|
||||
public static void setTransactionDefinition(TransactionDefinition definition) {
|
||||
if (definition == null) {
|
||||
return;
|
||||
}
|
||||
int timeout = definition.getTimeout();
|
||||
|
||||
|
||||
Definition def = new Definition();
|
||||
def.setTimeout(timeout);
|
||||
def.setIsolationLevel(definition.getIsolationLevel());
|
||||
def.setPropagationBehavior(definition.getPropagationBehavior());
|
||||
def.setIsolationLevel(definition.getIsolationLevel());
|
||||
def.setName(definition.getName());
|
||||
TRANSACTION_DEFINITION.set(def);
|
||||
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
Date deadline = new Date(System.currentTimeMillis() + timeout * 1000L);
|
||||
TRANSACTION_DEADLINE.set(deadline);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除事务上下文
|
||||
*
|
||||
*/
|
||||
public static void clear() {
|
||||
TRANSACTION_DEFINITION.remove();
|
||||
TRANSACTION_DEADLINE.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前事务可用TTL
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static int getTimeToLiveInSeconds() {
|
||||
Date deadline = TRANSACTION_DEADLINE.get();
|
||||
if (deadline == null) {
|
||||
return 0;
|
||||
}
|
||||
double diff = ((double) getTimeToLiveInMillis(deadline)) / 1000;
|
||||
int secs = (int) Math.ceil(diff);
|
||||
checkTransactionTimeout(secs <= 0, deadline);
|
||||
return secs;
|
||||
}
|
||||
|
||||
private static void checkTransactionTimeout(boolean deadlineReached, Date deadline) throws TransactionTimedOutException {
|
||||
if (deadlineReached) {
|
||||
throw new TransactionTimedOutException("Transaction timed out: deadline was " + deadline);
|
||||
}
|
||||
}
|
||||
|
||||
private static long getTimeToLiveInMillis(Date deadline) throws TransactionTimedOutException {
|
||||
if (deadline == null) {
|
||||
throw new IllegalStateException("No timeout specified for this resource holder");
|
||||
}
|
||||
long timeToLive = deadline.getTime() - System.currentTimeMillis();
|
||||
checkTransactionTimeout(timeToLive <= 0, deadline);
|
||||
return timeToLive;
|
||||
}
|
||||
|
||||
private static class Definition implements TransactionDefinition {
|
||||
private Integer propagationBehavior;
|
||||
private Integer isolationLevel;
|
||||
private Integer timeout;
|
||||
private String name;
|
||||
private Boolean readOnly;
|
||||
|
||||
public void setReadOnly(Boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void setTimeout(Integer timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public void setIsolationLevel(Integer isolationLevel) {
|
||||
this.isolationLevel = isolationLevel;
|
||||
}
|
||||
|
||||
public void setPropagationBehavior(Integer propagationBehavior) {
|
||||
this.propagationBehavior = propagationBehavior;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPropagationBehavior() {
|
||||
return propagationBehavior == null ? TransactionDefinition.super.getPropagationBehavior() : propagationBehavior;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIsolationLevel() {
|
||||
return isolationLevel == null ? TransactionDefinition.super.getIsolationLevel() : isolationLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeout() {
|
||||
return timeout == null ? TransactionDefinition.super.getTimeout() : timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return readOnly == null ? TransactionDefinition.super.isReadOnly() : readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name == null ? TransactionDefinition.super.getName() : name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class AccountService {
|
||||
@ -33,11 +34,21 @@ public class AccountService {
|
||||
|
||||
@Transactional
|
||||
public void update2() {
|
||||
int x = 1/0;
|
||||
int x = 1 / 0;
|
||||
Account account = new Account();
|
||||
account.setId(2L);
|
||||
account.setUserName("haha");
|
||||
accountMapper.update(account);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class, timeout = 3)
|
||||
public void transactionTimeTest() throws InterruptedException {
|
||||
Account account = new Account();
|
||||
account.setId(100L);
|
||||
account.setUserName("aliothmoon");
|
||||
accountMapper.insert(account);
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
accountMapper.selectOneById(account.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user