系统中要考虑多数据源事务的情况,我已把功能大致实现, 测试也通过了,现在请教下大家看这样有什么潜在问题吗?

实现思路:

执行涉及多数据源业务操作前,依次开启每个数据源事务,如果执行过程中出现异常,依次回滚开启事务,否则全部提交。

使用方式:

通过自定义拦截器MultiTx.class使用,inventory与mes是要用到的两个数据源名称(MultiTx.class根据com.jfinal.plugin.activerecord.tx.Tx.java的基础上进行了调整

image.png


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++;
			}
		}
	}

}



评论区

JFinal

2019-05-07 16:11

多数据源事务属于分布式事务的范畴,不是这么简单就解决的,使用专门的中间件去解决分布式事务

热门反馈

扫码入社