package com.lizard.fastdb.transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.transaction.TransactionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectweb.jotm.Jotm;
import org.objectweb.jotm.TimerManager;
import org.objectweb.transaction.jta.TMService;
import com.lizard.fastdb.util.StringUtils;
/**
* 多数据源事务控制容器
*
* @author SHEN.GANG
*/
public class MultiTransactionController
{
private final static Log logger = LogFactory.getLog( MultiTransactionController.class );
/** 事务 */
private final static ThreadLocal<Transaction> LOCAL_TRANS = new ThreadLocal<Transaction>();
/** 事务所用数据库操作对象池 */
private final static ThreadLocal<Map<String,Connection>> LOCAL_CONNECTIONPOOL = new ThreadLocal<Map<String,Connection>>();
/** 负责事务数据操作提交、回滚的管理器 */
public static TransactionManager transMgr ;
/** 事务助手 */
private static TMService transMgr_service;
/**
* 开启事务
*
* @throws TransactionException
*/
public static void beginTransaction() throws TransactionException
{
// 判断事务嵌套模式,如果在单数据源事务内部使用了多数据源事务,则抛出异常
if(Transaction.getTransMode() == TransactionConstant.TRANS_MODE_SINGLEDATASOURCE)
{
throw new TransactionException("单数据源事务模式内部不能使用多数据源事务...");
}
Transaction _trans= LOCAL_TRANS.get();
try
{
// 判断此事务是否属于全局事务
if( _trans == null )
{
if(transMgr == null)
{
// 开启事务助手服务
transMgr_service = new Jotm(true,false);
transMgr = transMgr_service.getTransactionManager();
}
transMgr.begin();
_trans = new Transaction();
Transaction.setTransMode(TransactionConstant.TRANS_MODE_MULTIDATASOURCE);
// 全局事务所用数据库操作对象池
LOCAL_TRANS.set( _trans );
LOCAL_CONNECTIONPOOL.set(new HashMap<String,Connection>());
} else {
// 事务已经开启,将事务深度+1, 事务次数+1
_trans.setTransCount(_trans.getTransCount()+1);
_trans.setTransDeep(_trans.getTransDeep()+1);
}
}catch (Exception e)
{
logger.error("开启事务失败!");
throw new TransactionException("Error to start transaction!", e);
}
}
/**
* 提交事务
*
* @throws TransactionException
*/
public static void commitTransaction() throws TransactionException
{
// 判断事务嵌套模式,如果在单数据源事务内部使用了多数据源事务,则直接退出
if(Transaction.getTransMode() == TransactionConstant.TRANS_MODE_SINGLEDATASOURCE)
{
return;
}
Transaction _trans = LOCAL_TRANS.get();
// 提交次数+1
_trans.setCommitCount( _trans.getCommitCount()+1 );
try {
// 事务全部正确执行,提交数据
if( _trans.hasFullExecute())
{
transMgr.commit();
logger.info("多数据源事务管理器提交数据...");
}
} catch (Exception e) {
try {
transMgr.rollback();
logger.error("提交事务失败!");
throw new TransactionException("Error to commit transaction!", e);
} catch (Exception e1) {
logger.error("回滚事务失败!");
throw new TransactionException("Error to rollback transaction!",e1);
}
}
}
/**
* 关闭事务
*
* @throws TransactionException
*/
public static void closeTransaction() throws TransactionException
{
// 判断事务嵌套模式,如果在单数据源事务内部使用了多数据源事务,则直接退出
if(Transaction.getTransMode() == TransactionConstant.TRANS_MODE_SINGLEDATASOURCE)
{
return;
}
Transaction _trans = LOCAL_TRANS.get();
// 如果事务深度>1,则是嵌套事务,不关闭连接,直接将事务深度-1
if( _trans.getTransDeep()>1 )
{
_trans.setTransDeep(_trans.getTransDeep()-1);
return;
}
Map<String,Connection> connPool = LOCAL_CONNECTIONPOOL.get();
// 全局事务已经提交,清空ThreadLocal变量
LOCAL_TRANS.set( null );
LOCAL_CONNECTIONPOOL.set(null);
Transaction.setTransMode(null);
try
{
// 事务未全部正确执行,回滚数据
if( !_trans.hasFullExecute())
{
transMgr.rollback();
logger.info("多数据源事务管理器回滚数据...");
}
// 关闭事务助手服务
if(transMgr_service != null)
{
transMgr_service.stop();
TimerManager.stop();
transMgr_service = null;
}
// 释放所有数据库连接
//for(String ds_name:connPool.keySet())
for( Map.Entry<String,Connection> e : connPool.entrySet() )
{
Connection conn = e.getValue(); //connPool.get(ds_name);
if(conn != null && !conn.isClosed())
{
conn.close();
}
conn = null;
}
connPool = null;
_trans = null;
}
catch (Exception e)
{
logger.error("关闭事务失败!");
throw new TransactionException("Error to close transaction!", e);
}
}
/**
* 加载某数据源下的connection对象
*
* @param ds_name 数据源名称
* @param conn 数据库连接对象
*/
public static void load(String ds_name, Connection conn)
{
if( !StringUtils.isEmpty( ds_name ) && conn != null )
{
try {
conn.setAutoCommit( false );
} catch (SQLException e) {
e.printStackTrace();
}
MultiTransactionController.LOCAL_CONNECTIONPOOL.get().put(ds_name, conn);
}
}
/**
* 事务管理器是否已经控制某数据源
*
* @param ds_name 数据源名称
* @return true 已经控制、false 没有控制
*/
public static boolean contains(String ds_name)
{
return MultiTransactionController.LOCAL_CONNECTIONPOOL.get().containsKey(ds_name);
}
/**
* 获得事务管理器控制的某数据源下的数据库连接对象
*
* @param ds_name 数据源名称
* @return 数据连接
*/
public static Connection getConnection(String ds_name)
{
return MultiTransactionController.LOCAL_CONNECTIONPOOL.get().get(ds_name);
}
}