package org.nutz.dao.impl;
import java.io.Closeable;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;
import javax.sql.DataSource;
import org.nutz.dao.ConnCallback;
import org.nutz.dao.Dao;
import org.nutz.dao.DaoException;
import org.nutz.dao.impl.sql.run.NutDaoRunner;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
/**
* 独立于Trans事务的Dao实例. 本实例不是线程安全的,不可以在不同线程中同时操作, 但可以重复使用.
*
* @author wendal
*
*/
public class NutTxDao extends NutDao implements Closeable {
private static final Log log = Logs.get();
protected Connection conn;
protected String id;
protected Savepoint sp;
protected boolean debug;
/**
* 是否恢复conn原本的auto commit设置
*/
protected boolean _autoCommit;
protected NutMap sps;
/**
* 通过NutDao实例构建一个独立事务的Dao实例
*
* @param _dao
* 参数类型是Dao,但必须是NutDao实例
* @throws DaoException
* 获取数据连接失败时会抛出
*/
public NutTxDao(Dao _dao) throws DaoException {
NutDao dao = (NutDao) _dao;
// ---------------------------------
// 下面两个属性在1.r.59及之前的版本没有开放,但仍可以通过Mirror.set/get的方法操作
this.meta = dao.meta;
this.sqlManager = dao.sqlManager;
// ---------------------------------
this.dataSource = dao.dataSource;
this.executor = dao.executor;
this.dataSource = dao.dataSource;
this.expert = dao.expert;
this.pojoMaker = dao.pojoMaker;
this.holder = dao.holder;
this.autoTransLevel = dao.autoTransLevel;
this._interceptors = dao._interceptors;
this.setRunner(new NutDaoRunner() {
public void _run(DataSource dataSource, ConnCallback callback) {
try {
runCallback(getConnection(), callback);
}
catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
throw new DaoException(e);
}
}
});
}
/**
* 开启事务,级别为READ_COMMITTED
*
* @return 原对象
*/
public NutTxDao beginRC() {
return begin(Connection.TRANSACTION_READ_COMMITTED);
}
/**
* 开启事务,级别为SERIALIZABLE
*
* @return 原对象
*/
public NutTxDao beginSE() {
return begin(Connection.TRANSACTION_SERIALIZABLE);
}
/**
* 开启事务
*
* @param transLevel
* 事务级别
* @see java.sql.Connection
* @return 原对象
* @throws DaoException
* 如果已经开启过事务
*/
public NutTxDao begin(int transLevel) throws DaoException {
if (this.conn != null)
throw new DaoException("NutTxDao has been begined!!");
id = R.UU32();
if (debug)
log.debugf("begin level=%d id=%s", transLevel, id);
try {
this.conn = dataSource.getConnection();
this.conn.setTransactionIsolation(transLevel);
if (this.conn.getAutoCommit() == true) {
this.conn.setAutoCommit(false);
_autoCommit = true;
}
setSavepoint(id);
}
catch (SQLException e) {
throw new DaoException(e);
}
return this;
}
/**
* 提交事务
*
* @return 原对象
*/
public NutTxDao commit() {
if (debug)
log.debugf("commit id=%s", id);
try {
conn.commit();
}
catch (SQLException e) {
throw new DaoException(e);
}
return this;
}
/**
* 回滚事务
*
* @return 原对象
*/
public NutTxDao rollback() {
return rollback(id);
}
/**
* 回滚事务
*
* @param id
* 回滚点
* @return 原对象
*/
public NutTxDao rollback(String id) {
if (debug)
log.debugf("rollback id=%s", id);
try {
Savepoint sp = null;
if (this.id.equals(id))
sp = this.sp;
else if (sps != null) {
sp = sps.getAs(id, Savepoint.class);
}
if (sp != null)
conn.rollback(sp);
else
log.debug("Null Savepoint found, skip, id=" + id);
}
catch (Throwable e) {
}
return this;
}
public NutTxDao setSavepoint(String spId) {
try {
Savepoint sp = conn.setSavepoint(spId);
if (id.equals(spId))
this.sp = sp;
else {
if (sps == null)
sps = new NutMap();
sps.put(spId, sp);
}
}
catch (SQLException e) {
throw new DaoException(e);
}
return this;
}
/**
* 关闭事务及连接
*/
public void close() {
if (debug)
log.debugf("close id=%s", id);
try {
if (conn == null)
return;
if (_autoCommit)
conn.setAutoCommit(true);
conn.close();
conn = null;
}
catch (Throwable e) {
}
}
/**
* 获取当前事务的数据库连接
*
* @return 数据库连接
*/
public Connection getConnection() {
return conn;
}
/**
* 本事务的id
*
* @return 一个字符串
*/
public String getId() {
return id;
}
/**
* 设置debug默认
*
* @param debug
* 是否开启debug日志
*/
public NutTxDao setDebug(boolean debug) {
this.debug = debug;
return this;
}
protected void finalize() throws Throwable {
close();
super.finalize();
}
}