TransactionalManager add Propagation support

This commit is contained in:
开源海哥 2023-04-16 15:39:41 +08:00
parent 745c89683e
commit 9f433566be
4 changed files with 179 additions and 27 deletions

View File

@ -22,7 +22,7 @@ import com.mybatisflex.core.query.CPI;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.transaction.TransactionContext;
import com.mybatisflex.core.transaction.Propagation;
import com.mybatisflex.core.transaction.TransactionalManager;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.util.MapUtil;
@ -563,31 +563,12 @@ public class Db {
* @param supplier
*/
public static boolean tx(Supplier<Boolean> supplier) {
//上一级事务的id支持事务嵌套
String higherXID = TransactionContext.getXID();
try {
String xid = TransactionalManager.startTransactional();
Boolean success = false;
boolean rollbacked = false;
try {
success = supplier.get();
} catch (Exception e) {
rollbacked = true;
TransactionalManager.rollback(xid);
e.printStackTrace();
} finally {
if (success != null && success) {
TransactionalManager.commit(xid);
} else if (!rollbacked) {
TransactionalManager.rollback(xid);
}
}
return success != null && success;
} finally {
//恢复上一级事务
if (higherXID != null) {
TransactionContext.hold(higherXID);
}
}
return tx(supplier, Propagation.REQUIRED);
}
public static boolean tx(Supplier<Boolean> supplier, Propagation propagation) {
Boolean result = TransactionalManager.exec(supplier, propagation);
return result != null && result;
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.transaction;
/**
* 事务的传递方式参考 spring
*/
public enum Propagation {
//若存在当前事务则加入当前事务若不存在当前事务则创建新的事务
REQUIRED(0),
//若存在当前事务则加入当前事务若不存在当前事务则已非事务的方式运行
SUPPORTS(1),
//若存在当前事务则加入当前事务若不存在当前事务则抛出异常
MANDATORY(2),
//始终以新事物的方式运行若存在当前事务则暂停挂起当前事务
REQUIRES_NEW(3),
//以非事物的方式运行若存在当前事务则暂停挂起当前事务
NOT_SUPPORTED(4),
//以非事物的方式运行若存在当前事务则抛出异常
NEVER(5),
//如果存在当前事务则在嵌套事务中执行否则行为类似于 PROPAGATION_REQUIRED
NESTED(6),
;
private int value;
Propagation(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.transaction;
public class TransactionException extends RuntimeException {
public TransactionException(String message) {
super(message);
}
public TransactionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -23,6 +23,7 @@ import java.sql.SQLException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
* 事务管理器
@ -59,6 +60,90 @@ public class TransactionalManager {
}
public static Boolean exec(Supplier<Boolean> supplier, Propagation propagation) {
//上一级事务的id支持事务嵌套
String currentXID = TransactionContext.getXID();
try {
switch (propagation) {
//若存在当前事务则加入当前事务若不存在当前事务则创建新的事务
case REQUIRED:
if (currentXID != null) {
return supplier.get();
} else {
return execNewTransactional(supplier);
}
//若存在当前事务则加入当前事务若不存在当前事务则已非事务的方式运行
case SUPPORTS:
return supplier.get();
//若存在当前事务则加入当前事务若不存在当前事务则已非事务的方式运行
case MANDATORY:
if (currentXID != null) {
return supplier.get();
} else {
throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
//始终以新事物的方式运行若存在当前事务则暂停挂起当前事务
case REQUIRES_NEW:
return execNewTransactional(supplier);
//以非事物的方式运行若存在当前事务则暂停挂起当前事务
case NOT_SUPPORTED:
if (currentXID != null) {
TransactionContext.release();
}
return supplier.get();
//以非事物的方式运行若存在当前事务则抛出异常
case NEVER:
if (currentXID != null) {
throw new TransactionException("Existing transaction found for transaction marked with propagation 'never'");
}
return supplier.get();
//暂时不支持这种事务传递方式
//default nested 方式
default:
throw new TransactionException("Transaction manager does not allow nested transactions");
}
} finally {
//恢复上一级事务
if (currentXID != null) {
TransactionContext.hold(currentXID);
}
}
}
private static Boolean execNewTransactional(Supplier<Boolean> supplier) {
String xid = TransactionalManager.startTransactional();
Boolean success = false;
boolean rollbacked = false;
try {
success = supplier.get();
} catch (Exception e) {
rollbacked = true;
TransactionalManager.rollback(xid);
throw new TransactionException(e.getMessage(), e);
} finally {
if (success != null && success) {
TransactionalManager.commit(xid);
} else if (!rollbacked) {
TransactionalManager.rollback(xid);
}
}
return success;
}
public static Connection getConnection(String xid, String ds) {
Map<String, Connection> connections = CONNECTION_HOLDER.get().get(xid);
return connections == null || connections.isEmpty() ? null : connections.get(ds);