Merge pull request #424 from Aliothmoon/fix/issue-418

fix: 修复事务超时时间问题 & 提供更完善的Spring事务定义上下文
This commit is contained in:
Michael Yang 2024-10-29 09:18:32 +08:00 committed by GitHub
commit 657cb59b79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 165 additions and 2 deletions

View File

@ -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)
);
}
/**

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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());
}
}