package com.lizard.fastdb.transaction; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.lizard.fastdb.util.StringUtils; /** * 单数据源事务控制容器 * * @author SHEN.GANG */ public class TransactionController { private final static Log logger = LogFactory.getLog( TransactionController.class ); /** 事务 */ private final static ThreadLocal<Transaction> LOCAL_TRANS = new ThreadLocal<Transaction>(); /** 事务所用数据库连接对象 */ private final static ThreadLocal<Connection> LOCAL_CONNECTION = new ThreadLocal<Connection>(); /** 事务所用数据源 */ public final static ThreadLocal<String> LOCAL_DSNAME = new ThreadLocal<String>(); /** * 开启事务 * * @throws TransactionException */ public static void beginTransaction() throws TransactionException { // 判断事务嵌套模式,多数据事务模式内部使用单数据源事务,自动切换为多数据源事务模式 if(Transaction.getTransMode() == TransactionConstant.TRANS_MODE_MULTIDATASOURCE) { MultiTransactionController.beginTransaction(); return ; } Transaction _trans= LOCAL_TRANS.get(); try { // 判断此事务是否属于全局事务 if( _trans == null ) { _trans = new Transaction(); Transaction.setTransMode(TransactionConstant.TRANS_MODE_SINGLEDATASOURCE); LOCAL_TRANS.set( _trans ); }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_MULTIDATASOURCE) { MultiTransactionController.commitTransaction(); return ; } Transaction _trans = LOCAL_TRANS.get(); // 将提交次数+1 _trans.setCommitCount(_trans.getCommitCount()+1); Connection conn = LOCAL_CONNECTION.get(); try { // 事务全部正确执行,提交数据 if( _trans.hasFullExecute()) { if(conn != null) { conn.commit(); logger.info("单数据源事务管理器提交数据,数据源【"+LOCAL_DSNAME.get()+"】..."); } } } catch (Exception e) { try { // 提交数据失败,回滚数据 conn.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_MULTIDATASOURCE) { MultiTransactionController.closeTransaction(); return ; } Transaction _trans = LOCAL_TRANS.get(); // 如果事务深度>1,则是嵌套事务,不关闭连接,直接将事务深度-1 if( _trans.getTransDeep()>1 ) { _trans.setTransDeep(_trans.getTransDeep()-1); return; } Connection conn = LOCAL_CONNECTION.get(); String ds_name = LOCAL_DSNAME.get(); // 全局事务已经提交,清空ThreadLocal变量 LOCAL_TRANS.set( null ); LOCAL_CONNECTION.set(null); LOCAL_DSNAME.set(null); Transaction.setTransMode(null); try { // 事务未全部正确执行,回滚数据 if( !_trans.hasFullExecute()) { if(conn != null) { conn.rollback(); logger.info("单数据源事务管理器回滚数据,数据源【"+ds_name+"】"); } } // 释放数据库连接 if(conn!=null && !conn.isClosed()) { conn.close(); } conn = 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(); } LOCAL_CONNECTION.set( conn ); LOCAL_DSNAME.set( ds_name ); } } /** * 事务管理器是否已经控制某数据源 * * @param ds_name 数据源名称 * @return true 已经控制、false 没有控制 */ public static boolean contains(String ds_name) { if( !StringUtils.isEmpty( ds_name )) { return ds_name.equals( LOCAL_DSNAME.get() ); } return false ; } /** * 获得事务管理器控制的数据库连接对象 * * @return 当前线程中保存的数据库连接 */ public static Connection getConnection() { return LOCAL_CONNECTION.get(); } /** * 判读事务管理器是否已经被启用 * * @return true -- 已经被使用, false -- 未被使用 */ public static boolean isUsed() { return !StringUtils.isEmpty( LOCAL_DSNAME.get() ) && ( LOCAL_CONNECTION != null ); } }