diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java index e6174b6a..bec05aaa 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java @@ -19,13 +19,19 @@ import com.mybatisflex.core.dialect.DbType; import com.mybatisflex.core.dialect.DbTypeUtil; import com.mybatisflex.core.transaction.TransactionContext; import com.mybatisflex.core.transaction.TransactionalManager; +import com.mybatisflex.core.util.ArrayUtil; import com.mybatisflex.core.util.StringUtil; import javax.sql.DataSource; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Objects; public class FlexDataSource extends AbstractDataSource { @@ -64,7 +70,7 @@ public class FlexDataSource extends AbstractDataSource { if (connection != null) { return connection; } else { - connection = getDataSource().getConnection(); + connection = proxy(getDataSource().getConnection(), xid); TransactionalManager.hold(xid, dataSourceKey, connection); return connection; } @@ -86,7 +92,7 @@ public class FlexDataSource extends AbstractDataSource { if (connection != null) { return connection; } else { - connection = getDataSource().getConnection(username, password); + connection = proxy(getDataSource().getConnection(username, password), xid); TransactionalManager.hold(xid, dataSourceKey, connection); return connection; } @@ -95,6 +101,12 @@ 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)); + } + @Override @SuppressWarnings("unchecked") @@ -125,5 +137,31 @@ public class FlexDataSource extends AbstractDataSource { return dataSource; } + private static class ConnectionHandler implements InvocationHandler { + private static String[] proxyMethods = new String[]{"commit", "rollback", "close",}; + private Connection original; + private String xid; + + public ConnectionHandler(Connection original, String xid) { + this.original = original; + this.xid = xid; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (ArrayUtil.contains(proxyMethods, method.getName()) + && isTransactional()) { + //do nothing + return null; + } + System.out.println(">>>>>>invoke: " + method.getName() + " args: " + Arrays.toString(args)); + return method.invoke(original, args); + } + + private boolean isTransactional() { + return Objects.equals(xid, TransactionContext.getXID()); + } + } + } 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 49e047f5..6235cba0 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 @@ -399,23 +399,26 @@ public class Db { try { String xid = UUID.randomUUID().toString(); TransactionContext.hold(xid); - boolean success = false; + Boolean success = false; boolean rollbacked = false; try { success = supplier.get(); } catch (Exception e) { rollbacked = true; + TransactionContext.release(); TransactionalManager.rollback(xid); e.printStackTrace(); } finally { - if (success) { + if (success != null && success) { + //必须优先 release 掉 xid,才能正常 commit() + TransactionContext.release(); TransactionalManager.commit(xid); } else if (!rollbacked) { + TransactionContext.release(); TransactionalManager.rollback(xid); } - TransactionContext.release(); } - return success; + return success != null && success; } finally { //恢复上一级事务 if (prevXID != null) { 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 316c89c6..29857cf2 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 @@ -53,6 +53,7 @@ public class TransactionalManager { log.debug("Error set AutoCommit to false. Cause: " + e); } } + connMap.put(ds, connection); } diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MultiDataSourceTester.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MultiDataSourceTester.java new file mode 100644 index 00000000..b38e90a4 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/MultiDataSourceTester.java @@ -0,0 +1,73 @@ +/** + * 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.test;
+
+import com.mybatisflex.core.MybatisFlexBootstrap;
+import com.mybatisflex.core.datasource.DataSourceKey;
+import com.mybatisflex.core.row.Db;
+import com.mybatisflex.core.row.Row;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
+import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
+
+import javax.sql.DataSource;
+import java.util.List;
+
+public class MultiDataSourceTester {
+
+ public static void main(String[] args) {
+ DataSource dataSource = new EmbeddedDatabaseBuilder()
+ .setType(EmbeddedDatabaseType.H2)
+ .setName("db1")
+ .addScript("schema.sql")
+ .addScript("data.sql")
+ .build();
+
+ DataSource dataSource2 = new EmbeddedDatabaseBuilder()
+ .setType(EmbeddedDatabaseType.H2)
+ .setName("db2")
+ .addScript("schema02.sql")
+ .addScript("data02.sql")
+ .build();
+
+ MybatisFlexBootstrap.getInstance()
+ .setDataSource(dataSource)
+ .addDataSource("ds2", dataSource2)
+ .start();
+
+ //默认查询 db1
+ List