package cn.dreampie.common.plugin.db.tx; /** * Created by wangrenhui on 2014/5/9. */ import com.jfinal.aop.Interceptor; import com.jfinal.core.ActionInvocation; import com.jfinal.plugin.activerecord.ActiveRecordException; import com.jfinal.plugin.activerecord.Config; import com.jfinal.plugin.activerecord.DbKit; import javax.sql.XAConnection; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.sql.Connection; import java.sql.SQLException; /** * ActiveRecord declare transaction. * Example: @Before(Tx.class) */ public class XATx implements Interceptor { static Config[] getConfigWithAXTxConfigs(ActionInvocation ai) { AXTxConfig axTxConfig = ai.getMethod().getAnnotation(AXTxConfig.class); if (axTxConfig == null) axTxConfig = ai.getController().getClass().getAnnotation(AXTxConfig.class); if (axTxConfig != null) { Config[] configs = null; String[] names = axTxConfig.value(); if (names != null && names.length > 0) { int length = names.length; configs = new Config[length]; int i = 0; for (String conf : names) { configs[i] = DbKit.getConfig(conf); i++; } } if (configs == null) throw new RuntimeException("Config not found with AXTxConfig"); return configs; } return null; } protected int getTransactionLevel(Config config) { return config.getTransactionLevel(); } static int tid = 0; public void intercept(ActionInvocation ai) { Config[] configs = getConfigWithAXTxConfigs(ai); XAXid xid = XAXid.uniqueXid(++tid); if (configs != null && configs.length > 0) { boolean r = true; XAResult[] xaResults = new XAResult[configs.length]; int i = 0; for (Config c : configs) { //当前配置的数据源的预执行结果 xaResults[i] = prepare(ai, c, xid); r = r && (xaResults[i].getXaResource() != null); i++; } //如果执行没有错误 if (r) { Connection connection = null; for (XAResult xaResult : xaResults) { connection = ((Connection) xaResult.getXaConnection()); try { xaResult.getXaResource().commit(xid, false); } catch (XAException xae) { throw new ActiveRecordException(xae); } finally { try { if (connection != null) { if (xaResult.getAutoCommit() != null) { connection.setAutoCommit(xaResult.getAutoCommit()); } } } catch (Exception e) { e.printStackTrace(); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown } finally { if (xaResult.isRemoveConnection()) xaResult.getConfig().removeThreadLocalConnection(); // prevent memory leak } } } } } tid--; } private XAResult prepare(ActionInvocation ai, Config config, Xid xid) { XAConnection xaConn = null; Connection conn = config.getThreadLocalConnection(); if (conn != null) { // Nested transaction support xaConn = (XAConnection) conn; try { if (conn.getTransactionIsolation() < getTransactionLevel(config)) conn.setTransactionIsolation(getTransactionLevel(config)); //返回resource return new XAResult(config, prepareInvoke(xaConn, xid, ai), xaConn, null, false); } catch (SQLException e) { throw new ActiveRecordException(e); } catch (XAException xae) { throw new ActiveRecordException(xae); } } Boolean autoCommit = null; try { conn = config.getConnection(); autoCommit = conn.getAutoCommit(); config.setThreadLocalConnection(conn); conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel); conn.setAutoCommit(false); xaConn = (XAConnection) conn; return new XAResult(config, prepareInvoke(xaConn, xid, ai), xaConn, autoCommit, true); } catch (Exception e) { throw new ActiveRecordException(e); } } private XAResource prepareInvoke(XAConnection xaconn, Xid xid, ActionInvocation ai) throws SQLException, XAException { XAResource xaRes = xaconn.getXAResource(); xaRes.start(xid, XAResource.TMNOFLAGS); //操作 ai.invoke(); xaRes.end(xid, XAResource.TMSUCCESS); int result = xaRes.prepare(xid); if (result == XAResource.XA_OK) { return xaRes; } return null; } }