diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java index 8e5f5998..40db164e 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java @@ -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 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 supplier, Propagation propagation) { + Boolean result = TransactionalManager.exec(supplier, propagation); + return result != null && result; } } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/Propagation.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/Propagation.java new file mode 100644 index 00000000..e19fcaa1 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/Propagation.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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; + } +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionException.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionException.java new file mode 100644 index 00000000..1e736d63 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionException.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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); + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionalManager.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionalManager.java index b123eab0..ac5cf941 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionalManager.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/transaction/TransactionalManager.java @@ -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 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 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 connections = CONNECTION_HOLDER.get().get(xid); return connections == null || connections.isEmpty() ? null : connections.get(ds);