package org.nutz.trans;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.log.Log;
import org.nutz.log.Logs;
/**
* 用模板的方式操作事务
*
* @author zozoh(zozohtnt@gmail.com)
* @author wendal(wendal1985@gmail.com)
*/
public abstract class Trans {
private static final Log log = Logs.get();
private static Class<? extends Transaction> implClass;
static ThreadLocal<Transaction> trans = new ThreadLocal<Transaction>();
static ThreadLocal<Integer> count = new ThreadLocal<Integer>();
/**
* 事务debug开关
*/
public static boolean DEBUG = false;
/**
* @return 当前线程的事务,如果没有事务,返回 null
*/
public static Transaction get() {
return trans.get();
}
/**
* 这个函数允许你扩展默认的 Nutz 事务实现方式
*
* @param classOfTransaction
* 你的事务实现
*/
public static void setup(Class<? extends Transaction> classOfTransaction) {
implClass = classOfTransaction;
}
static void _begain(int level) throws Exception {
Transaction tn = trans.get();
if (null == tn) {
tn = null == implClass ? new NutTransaction() : Mirror.me(implClass).born();
tn.setLevel(level);
trans.set(tn);
count.set(0);
if (DEBUG)
log.debugf("Start New Transaction id=%d, level=%d", tn.getId(), level);
} else {
if (DEBUG)
log.debugf("Attach Transaction id=%d, level=%d", tn.getId(), level);
}
int tCount = count.get() + 1;
count.set(tCount);
//if (DEBUG)
// log.debugf("trans_begain: %d", tCount);
}
static void _commit() throws Exception {
count.set(count.get() - 1);
Transaction tn = trans.get();
if (count.get() == 0) {
if (DEBUG)
log.debug("Transaction Commit id="+tn.getId());
tn.commit();
} else {
if (DEBUG)
log.debugf("Transaction delay Commit id=%d, count=%d", tn.getId(), count.get());
}
}
static void _depose() {
if (count.get() == 0)
try {
if (DEBUG)
log.debugf("Transaction depose id=%d, count=%s", trans.get().getId(), count.get());
trans.get().close();
}
catch (Throwable e) {
throw Lang.wrapThrow(e);
}
finally {
trans.set(null);
}
}
static void _rollback(Integer num) {
count.set(num);
if (count.get() == 0) {
if (DEBUG)
log.debugf("Transaction rollback id=%s, count=%s", trans.get().getId(), num);
trans.get().rollback();
} else {
if (DEBUG)
log.debugf("Transaction delay rollback id=%s, count=%s", trans.get().getId(), num);
}
}
public static boolean isTransactionNone() {
Transaction t = trans.get();
return null == t || t.getLevel() == Connection.TRANSACTION_NONE;
}
/**
* 执行一组原子操作,默认的事务级别为: TRANSACTION_READ_COMMITTED。详细请看 exec(int level,
* Atom... atoms) 函数的说明
*
* @param atoms
* 原子操作对象
*/
public static void exec(Atom... atoms) {
exec(Connection.TRANSACTION_READ_COMMITTED, atoms);
}
/**
* 执行一组原子操作,并指定事务级别。
* <p>
* 这里需要注意的是,Nutz 支持事务模板的无限层级嵌套。 这里,如果每一层嵌套,指定的事务级别有所不同,不同的数据库,可能引发不可预知的错误。
* <p>
* 所以,嵌套的事务模板的事务,将以最顶层的事务为级别为标准。就是说,如果最顶层的事务级别为
* 'TRANSACTION_READ_COMMITTED',那么下面所包含的所有事务,无论你指定什么样的事务级别,都是
* 'TRANSACTION_READ_COMMITTED', 这一点,由抽象类 Transaction 来保证。其 setLevel
* 当被设置了一个大于 0 的整数以后,将不再 接受任何其他的值。
* <p>
* 你可以通过继承 Transaction 来修改这个默认的行为,当然,这个行为修改一般是没有必要的。
* <p>
* 另外,你还可能需要知道,通过 Trans.setup 方法,能让整个虚拟机的 Nutz 事务操作都使用你的 Transaction 实现
*
* @param level
* 事务的级别。
* <p>
* 你可以设置的事务级别是:
* <ul>
* <li>java.sql.Connection.TRANSACTION_NONE
* <li>java.sql.Connection.TRANSACTION_READ_UNCOMMITTED
* <li>java.sql.Connection.TRANSACTION_READ_COMMITTED
* <li>java.sql.Connection.TRANSACTION_REPEATABLE_READ
* <li>java.sql.Connection.TRANSACTION_SERIALIZABLE
* </ul>
* 不同的数据库,对于 JDBC 事务级别的规范,支持的力度不同。请参看相应数据库的文档,已
* 确定你设置的数据库事务级别是否被支持。
* @param atoms
* 原子操作对象
* @see org.nutz.trans.Transaction
* @see java.sql.Connection
*/
public static void exec(int level, Atom... atoms) {
if (null == atoms)
return;
int num = count.get() == null ? 0 : count.get();
try {
_begain(level);
for (Atom atom : atoms)
atom.run();
_commit();
}
catch (Throwable e) {
_rollback(num);
throw Lang.wrapThrow(e);
}
finally {
_depose();
}
}
/**
* 执行一个分子,并给出返回值
*
* @param <T>
* @param molecule
* 分子
* @return 分子返回值
*/
public static <T> T exec(Molecule<T> molecule) {
Trans.exec((Atom) molecule);
return molecule.getObj();
}
/* ===========================下面暴露几个方法给喜欢 try...catch...finally 的人 ===== */
/**
* 开始一个事务,级别为 TRANSACTION_READ_COMMITTED
* <p>
* 你需要手工用 try...catch...finally 来保证你提交和关闭这个事务
*
* @throws Exception
*/
public static void begin() throws Exception {
Trans._begain(Connection.TRANSACTION_READ_COMMITTED);
}
/**
* 开始一个指定事务
* <p>
* 你需要手工用 try...catch...finally 来保证你提交和关闭这个事务
*
* @param level
* 指定级别
*
* @throws Exception
*/
public static void begin(int level) throws Exception {
Trans._begain(level);
}
/**
* 提交事务,执行它前,你必需保证你已经手工开始了一个事务
*
* @throws Exception
*/
public static void commit() throws Exception {
Trans._commit();
}
/**
* 回滚事务,执行它前,你必需保证你已经手工开始了一个事务
*
* @throws Exception
*/
public static void rollback() throws Exception {
Integer c = Trans.count.get();
if (c == null)
c = Integer.valueOf(0);
else if (c > 0)
c--;
Trans._rollback(c);
}
/**
* 关闭事务,执行它前,你必需保证你已经手工开始了一个事务
*
* @throws Exception
*/
public static void close() throws Exception {
Trans._depose();
}
/**
* 如果在事务中,则返回事务的连接,否则直接从数据源取一个新的连接
*/
public static Connection getConnectionAuto(DataSource ds) throws SQLException {
if (get() == null)
return ds.getConnection();
else
return get().getConnection(ds);
}
public static void closeConnectionAuto(Connection conn) {
if (get() == null && null != conn) {
try {
conn.close();
}
catch (SQLException e) {
throw Lang.wrapThrow(e);
}
}
}
}