事务声明分离单独使用

请波总 @JFinal 看看是否有问题,我用ab测试1000访问,10并发,感觉没有看出有问题。

使用Tx.Class的唯一问题的,如果业务想回滚,而不是Action执行出错的话,没办法回滚,另外就是违反了尽可能晚的声明事务,尽可能早的提交事务的原则,因此我查看了Tx的代码,把相关代码提取出来了,使用方法如下:

TxKit txKit = new TxKit();
txKit.Begin();
txKit.Rollback();
txKit.Commit();

TxKit的代码如下:

public class TxKit {
	private Boolean originAutoCommit = null;
	private Config config = null;
	private Connection conn = null;
	
	public boolean Begin() {
		config = DbKit.getConfig();		
		conn = config.getThreadLocalConnection();
		if (conn != null) {	// Nested transaction support
			try {
				if (!conn.isClosed() && conn.getTransactionIsolation() < config.getTransactionLevel())
					conn.setTransactionIsolation(config.getTransactionLevel());
				return true;
			} catch (SQLException e) {
				throw new ActiveRecordException(e);
			}
		}
		
		try {
			conn = config.getConnection();
			originAutoCommit = conn.getAutoCommit();
			config.setThreadLocalConnection(conn);
			conn.setTransactionIsolation(config.getTransactionLevel());	// conn.setTransactionIsolation(transactionLevel);
			conn.setAutoCommit(false);
			return true;
		} catch (Exception e) {
			if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e.getMessage(), e);}
			try {
				if (conn != null) {
					if (originAutoCommit != null)
						conn.setAutoCommit(originAutoCommit);
					conn.close();
				}
				return false;
			} catch (Throwable t) {
				// can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
				LogKit.error(t.getMessage(), t);
				return false;
			}
		}
	}
	
	public boolean Commit() {
		try {
			conn.commit();
			if (conn != null) {
				if (originAutoCommit != null)
					conn.setAutoCommit(originAutoCommit);
				conn.close();
			}
			return true;
		} catch (Exception e) {
			try {
				if (conn != null) {
					if (originAutoCommit != null)
						conn.setAutoCommit(originAutoCommit);
					conn.close();
					conn = null;
				}
				return false;
			} catch (Exception e2) {
				return false;
			}
		}
		finally {
			config.removeThreadLocalConnection();
		}
	}

	public boolean Rollback() {
		try {
			conn.rollback();
			if (conn != null) {
				if (originAutoCommit != null)
					conn.setAutoCommit(originAutoCommit);
				conn.close();
			}
			return true;
		} catch (Exception e) {
			try {
				if (conn != null) {
					if (originAutoCommit != null)
						conn.setAutoCommit(originAutoCommit);
					conn.close();
				}
				return false;
			} catch (Exception e2) {
				return false;
			}
		}
		finally {
			config.removeThreadLocalConnection();
		}
	}
}


评论区

杜福忠

2018-01-23 22:09

如果想在其他地方让 Tx.clsss 起作用的话, 需要使用 Duang.duang()、Enhancer.enhance() 了, 详见手册:

杜福忠

2018-01-23 22:27

Db.tx() 感觉也挺棒的, 个人感觉比 Tx.clsss 好用。。。

boolean tx = Db.tx(new IAtom() {

@Override
public boolean run() throws SQLException {
try {
// 处理业务
return true;
} catch (Exception e) {
log.error("xxx失败", e);
return false;
}
}
});

杜福忠

2018-01-23 22:32

Db.tx() 源码搬过来,我们一起来瞅瞅

/**
* Execute transaction.
* @param config the Config object
* @param transactionLevel the transaction level
* @param atom the atom operation
* @return true if transaction executing succeed otherwise false
*/
boolean tx(Config config, int transactionLevel, IAtom atom) {
Connection conn = config.getThreadLocalConnection();
if (conn != null) { // Nested transaction support
try {
if (conn.getTransactionIsolation() < transactionLevel)
conn.setTransactionIsolation(transactionLevel);
boolean result = atom.run();
if (result)
return true;
throw new NestedTransactionHelpException("Notice the outer transaction that the nested transaction return false"); // important:can not return false
}
catch (SQLException e) {
throw new ActiveRecordException(e);
}
}

Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
boolean result = atom.run();
if (result)
conn.commit();
else
conn.rollback();
return result;
} catch (NestedTransactionHelpException e) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
LogKit.logNothing(e);
return false;
} catch (Throwable t) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t);
} finally {
try {
if (conn != null) {
if (autoCommit != null)
conn.setAutoCommit(autoCommit);
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 {
config.removeThreadLocalConnection(); // prevent memory leak
}
}
}

简单代码

2018-01-24 08:59

@杜福忠 我试过Duang在服务上,没成功,db.tx主要会产生匿名类,而且还存在变量可用范围的问题,如果我检查完变量,想提交的时候,db.tx就需要变量是final的,否则必须在其内创建变量,这样就过早打开了事务,虽然没有多少代码,但是业务量大,事务多的应用还是可能有意想不到的结果。

杜福忠

2018-01-24 09:49

@简单代码 嗯,很多工具 是根据业务走,才能体会到它的好处

热门分享

扫码入社