实现思路:
在执行涉及多数据源业务操作前,依次开启每个数据源事务,如果执行过程中出现异常,依次回滚开启事务,否则全部提交。
使用方式:
通过自定义拦截器MultiTx.class使用,inventory与mes是要用到的两个数据源名称(MultiTx.class根据com.jfinal.plugin.activerecord.tx.Tx.java的基础上进行了调整)
public class MultiTx implements Interceptor{ public static Config[] getConfigWithTxConfig(Invocation inv) { TxConfig txConfig = inv.getMethod().getAnnotation(TxConfig.class); if (txConfig == null) txConfig = inv.getTarget().getClass().getAnnotation(TxConfig.class); Config[] config = new Config[txConfig.value().split(",").length]; if (txConfig != null) { int i=0; for(String v : txConfig.value().split(",")){ config[i] = DbKit.getConfig(v); if (config[i] == null) throw new RuntimeException("Config not found with TxConfig: " + config[i]); i++; } } return config; } protected int getTransactionLevel(Config config) { return config.getTransactionLevel(); } public void intercept(Invocation inv) { Config[] configs = getConfigWithTxConfig(inv); Boolean[] autoCommits = new Boolean[configs.length]; Connection[] conns=new Connection[configs.length]; try { int i=0; for(Config config : configs){ Connection conn = config.getThreadLocalConnection(); if (conn != null) { // Nested transaction support try { if (conn.getTransactionIsolation() < getTransactionLevel(config)) conn.setTransactionIsolation(getTransactionLevel(config)); inv.invoke(); return ; } catch (SQLException e) { throw new ActiveRecordException(e); } } conn = config.getConnection(); autoCommits[i] = conn.getAutoCommit(); config.setThreadLocalConnection(conn); conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel); conn.setAutoCommit(false); conns[i]=conn; i++; } inv.invoke(); for(Connection conn : conns){ conn.commit(); } } catch (NestedTransactionHelpException e) { for(Connection conn : conns){ if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);} LogKit.logNothing(e); } } catch (Throwable t) { for(Connection conn : conns){ if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);} } throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t); } finally { int i=0; for(Connection conn : conns){ try { if (conn != null) { if (autoCommits[i] != null) conn.setAutoCommit(autoCommits[i]); conn.close(); } } catch (Throwable t) { LogKit.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown } finally { configs[i].removeThreadLocalConnection(); // prevent memory leak } i++; } } } }