/* * Copyright 2008 biaoping.yin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.frameworkset.orm.transaction; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import org.apache.log4j.Logger; import com.frameworkset.orm.annotation.TransactionType; /** * * <p>Title: TransactionManager</p> * * <p>Description: 实现数据库的事务框架</p> * * <p> * bboss workgroup * </p> * <p> * Copyright (c) 2007 * </p> * * @Date 2009-6-1 下午08:58:51 * @author biaoping.yin * @version 1.0 */ public class TransactionManager implements java.io.Serializable { private static final Logger log = Logger.getLogger(TransactionManager.class); static final ThreadLocal thread_local = new ThreadLocal(); private boolean started = false; private boolean committed = false; private boolean rollbacked = false; private JDBCTransaction context_tx = null; private JDBCTransaction suspend_tx = null; /** * 始终创建新事务 */ public static final TransactionType NEW_TRANSACTION = TransactionType.NEW_TRANSACTION; /** * 如果没有事务创建新事务,有事务加入当前事务 */ public static final TransactionType REQUIRED_TRANSACTION = TransactionType.REQUIRED_TRANSACTION; /** * 有事务就加入事务,没有不创建事务,默认情况 */ public static final TransactionType MAYBE_TRANSACTION = TransactionType.MAYBE_TRANSACTION; /** * 没有事务 */ public static final TransactionType NO_TRANSACTION = TransactionType.NO_TRANSACTION; /** * 读写事务 */ public static final TransactionType RW_TRANSACTION = TransactionType.RW_TRANSACTION; /** * 未知事务 */ public static final TransactionType UNKNOWN_TRANSACTION = TransactionType.UNKNOWN_TRANSACTION; private TransactionType currenttxtype = TransactionType.REQUIRED_TRANSACTION; private static final long serialVersionUID = -6716097342564237376l; /** * Starts a new transaction, and associate it with the calling thread. * * @throws TransactionException * * @throws javax.transaction.NotSupportedException * If the calling thread is already associated with a * transaction, and nested transactions are not supported. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public void begin() throws TransactionException { // if(this.committed ) // throw new TransactionException("事务已经提交"); // // if(this.rollbacked) // throw new TransactionException("事务已经回滚"); // if(this.started == true) // throw new TransactionException("事务已经启动,并且事务正在执行中。"); // // JDBCTransaction currentTx = (JDBCTransaction) thread_local.get(); // // if (currentTx != null) { // // currentTx.begin(); // // } else { // currentTx = new JDBCTransaction(this.currenttxtype); // thread_local.set(currentTx); // currentTx.begin(); // // } // this.started = true; begin(REQUIRED_TRANSACTION); } /** * Starts a new transaction, and associate it with the calling thread. * * @param int * tx_type (默认值为REQUIRED_TRANSACTION) * * * * 始终创建新事务 * * JDBCTransaction.public static final int NEW_TRANSACTION = 0; * * * 如果没有事务创建新事务,有事务加入当前事务(默认值) * * JDBCTransaction.public static final int REQUIRED_TRANSACTION = 2; * * * 有事务就加入事务,没有不创建事务,默认情况 * * JDBCTransaction.public static final int MAYBE_TRANSACTION = 3; * * 没有事务 * * JDBCTransaction.public static final int NO_TRANSACTION = 4; * @throws TransactionException * * @throws javax.transaction.NotSupportedException * If the calling thread is already associated with a * transaction, and nested transactions are not supported. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public void begin(TransactionType tx_type) throws TransactionException { if(this.committed ) throw new TransactionException("事务已经提交"); if(this.rollbacked) throw new TransactionException("事务已经回滚"); if(this.started == true) throw new TransactionException("事务已经启动,并且事务正在执行中。"); JDBCTransaction currentTx = (JDBCTransaction) thread_local.get(); currenttxtype = tx_type; switch (tx_type) { case NEW_TRANSACTION: if(currentTx == null) { JDBCTransaction newTx = new JDBCTransaction(NEW_TRANSACTION); thread_local.set(newTx); newTx.begin(); } else { this.context_tx = currentTx; JDBCTransaction newTx = new JDBCTransaction(currentTx,NEW_TRANSACTION); newTx.begin(); thread_local.set(newTx); } break; case RW_TRANSACTION: if (currentTx != null) { if(currentTx.getTXType() == RW_TRANSACTION) { currentTx.begin(); } else { throw new TransactionException("当前存在类型为["+ currentTx.getTXType() +"]的事务,与RW_TRANSACTION类型事务不兼容,无法创建RW_TRANSACTION类型事务。"); } } else { currentTx = new JDBCTransaction(RW_TRANSACTION); thread_local.set(currentTx); currentTx.begin(); } break; case MAYBE_TRANSACTION: if (currentTx == null) ; else currentTx.begin(); break; case REQUIRED_TRANSACTION: if (currentTx != null ) { if(currentTx.getTXType() != RW_TRANSACTION) { currentTx.begin(); } else { throw new TransactionException("当前存在类型为["+ currentTx.getTXType() +"]的事务,与REQUIRED_TRANSACTION类型事务不兼容,无法创建REQUIRED_TRANSACTION类型事务。"); } } else { currentTx = new JDBCTransaction(REQUIRED_TRANSACTION); thread_local.set(currentTx); currentTx.begin(); } break; case NO_TRANSACTION: try { suspend_tx = suspendAll(); } catch (SystemException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; default: } started = true; } private void assertTX() throws RollbackException { if(!this.started ) throw new RollbackException("事务没有开始"); if(this.committed ) throw new RollbackException("事务已经提交"); if(this.rollbacked) throw new RollbackException("事务已经回滚"); } /** * Commit the transaction associated with the calling thread. * * @throws javax.transaction.RollbackException * If the transaction was marked for rollback only, the * transaction is rolled back and this exception is thrown. * @throws IllegalStateException * If the calling thread is not associated with a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. * @throws javax.transaction.HeuristicMixedException * If a heuristic decision was made and some some parts of the * transaction have been committed while other parts have been * rolled back. * @throws javax.transaction.HeuristicRollbackException * If a heuristic decision to roll back the transaction was * made. * @throws SecurityException * If the caller is not allowed to commit this transaction. */ public void commit() throws RollbackException { assertTX() ; JDBCTransaction tx = getTransaction(); try { if(this.currenttxtype == NO_TRANSACTION) { try { this.resume(this.suspend_tx); } catch (InvalidTransactionException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IllegalStateException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (SystemException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } this.committed = true; return; } int status; if (tx == null) { //added by biaoping.yin on 2008.09.02 if(this.currenttxtype == MAYBE_TRANSACTION) { this.committed = true; return ; } //added end throw new IllegalStateException( "thread not associated with transaction"); } if(this.context_tx != null) { if(context_tx == tx) return; } status = tx.getStatus(); if (status == Status.STATUS_MARKED_ROLLBACK) { try { log.debug("Commit execute rolling back action."); //如果事务被标识为回滚,则直接回滚事务,提交过程转入回滚过程 this.rollback(); this.rollbacked = true; return ; } catch(Exception e) { this.rollbacked = true; } } else { try { tx.commit(); } catch (SecurityException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw new RollbackException(e.getMessage()); } catch (IllegalStateException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw new RollbackException(e.getMessage()); } catch (RollbackException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw e; } catch (HeuristicMixedException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw new RollbackException(e.getMessage()); } catch (HeuristicRollbackException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw new RollbackException(e.getMessage()); } catch (SystemException e) { try { this.rollback(); this.rollbacked = true; } catch(Exception ei) { this.rollbacked = true; } throw new RollbackException(e.getMessage()); } } } finally { // if (tx.wasCommitted() || flag || rollbacked) { // if (tx.wasCommitted() || tx.wasRolledBack()) { if (tx != null && tx.wasCommitted() ) { if(this.context_tx == null) { setTransaction(null); } else { this.setTransaction(context_tx); //this.context_tx = null; } } this.committed = true; } } /** * Rolls back the transaction associated with the calling thread. * * @throws IllegalStateException * If the transaction is in a state where it cannot be rolled * back. This could be because the calling thread is not * associated with a transaction, or because it is in the * {@link javax.transaction.Status#STATUS_PREPARED prepared state}. * @throws SecurityException * If the caller is not allowed to roll back this transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public void rollback() throws RollbackException { _rollback(true); } private void _rollback(boolean check) throws RollbackException { if(check) assertTX() ; if(this.currenttxtype == NO_TRANSACTION) { try { this.resume(this.suspend_tx); } catch (InvalidTransactionException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (IllegalStateException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (SystemException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } this.rollbacked = true; return; } JDBCTransaction tx = getTransaction(); if (tx == null) { //added by biaoping.yin on 2008.09.02 if(this.currenttxtype == MAYBE_TRANSACTION) { this.rollbacked = true; return; } //added end throw new IllegalStateException( "no transaction associated with thread or tx has been commited or rollbacked."); } if(this.context_tx != null) { if(context_tx == tx) //该条件是否会成立呢 { this.rollbacked = true; return; } } try { tx.rollback(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (SystemException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { this.rollbacked = true; if(tx != null && tx.wasRolledBack() ) { if(this.context_tx == null) { setTransaction(null); } else { this.setTransaction(this.context_tx); // this.context_tx = null; } } } } // /** // * Mark the transaction associated with the calling thread for rollback // * only. // * // * @throws IllegalStateException // * If the transaction is in a state where it cannot be rolled // * back. This could be because the calling thread is not // * associated with a transaction, or because it is in the // * {@link javax.transaction.Status#STATUS_PREPARED prepared state}. // * @throws javax.transaction.SystemException // * If the transaction service fails in an unexpected way. // */ // public void setRollbackOnly() throws IllegalStateException, SystemException { // // JDBCTransaction tx = getTransaction(); // if (tx == null) // throw new IllegalStateException( // "no transaction associated with calling thread"); // tx.setRollbackOnly(); // } /** * Get the status of the transaction associated with the calling thread. * * @return The status of the transaction. This is one of the * {@link javax.transaction.Status} constants. If no transaction is * associated with the calling thread, * {@link javax.transaction.Status#STATUS_NO_TRANSACTION} is * returned. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public int getStatus() { JDBCTransaction tx = getTransaction(); return tx != null ? tx.getStatus() : Status.STATUS_NO_TRANSACTION; } /** * Get the transaction associated with the calling thread. * * @return The transaction associated with the calling thread, or * <code>null</code> if the calling thread is not associated with * a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public static JDBCTransaction getTransaction() { JDBCTransaction tx = (JDBCTransaction) thread_local.get(); return tx; } /** * Change the transaction timeout for transactions started by the calling * thread with the {@link #begin()} method. * * @param seconds * The new timeout value, in seconds. If this parameter is * <code>0</code>, the timeout value is reset to the default * value. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public void setTransactionTimeout(int seconds) throws SystemException { throw new SystemException("not supported"); } /** * Suspend the association the calling thread has to a transaction, and * return the suspended transaction. When returning from this method, the * calling thread is no longer associated with a transaction. * * @return The transaction that the calling thread was associated with, or * <code>null</code> if the calling thread was not associated with * a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public JDBCTransaction suspend() throws SystemException { JDBCTransaction retval = getTransaction(); if(this.context_tx != null) { setTransaction(context_tx); } else { setTransaction(null); } return retval; } /** * Suspend the association the calling thread has to a transaction, and * return the suspended transaction. When returning from this method, the * calling thread is no longer associated with a transaction. * * @return The transaction that the calling thread was associated with, or * <code>null</code> if the calling thread was not associated with * a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public JDBCTransaction suspendAll() throws SystemException { JDBCTransaction retval = getTransaction(); setTransaction(null); return retval; } /** * Resume the association of the calling thread with the given transaction. * * @param tx * The transaction to be associated with the calling thread. * @throws javax.transaction.InvalidTransactionException * If the argument does not represent a valid transaction. * @throws IllegalStateException * If the calling thread is already associated with a * transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an unexpected way. */ public void resume(JDBCTransaction tx) throws InvalidTransactionException, IllegalStateException, SystemException { setTransaction(tx); } /** * Just used for unit tests * * @param tx */ protected void setTransaction(JDBCTransaction tx) { thread_local.set(tx); } /** * 如果成功销毁事务,则返回true * 如果没有需要销毁的事务则返回false * @return boolean */ public static boolean destroyTransaction() { JDBCTransaction tx = getTransaction(); if(tx != null) { tx.printStackTrace(); try { tx.rollback(); } catch (IllegalStateException e) { // TODO Auto-generated catch block //e.printStackTrace(); } catch (SystemException e) { // TODO Auto-generated catch block //e.printStackTrace(); } thread_local.set(null); return true; } return false; } /** * 获取当前事务的类型 * @return */ public TransactionType getCurrenttxtype() { return this.currenttxtype; } /** * 获取当前事务的类型名称 * @return */ public String getCurrenttxtypeName() { switch(currenttxtype) { case NEW_TRANSACTION: return "NEW_TRANSACTION"; case REQUIRED_TRANSACTION: return "REQUIRED_TRANSACTION"; case MAYBE_TRANSACTION: return "MAYBE_TRANSACTION"; case NO_TRANSACTION: return "NO_TRANSACTION"; case RW_TRANSACTION: return "RW_TRANSACTION"; default: return "UNKNOWN_TRANSACTION"; } } /** * 在final方法中调用,用来在出现异常时对事务资源进行回收,首先对事务进行回滚, * 然后回收资源,如果事务已经提交和回滚,则不做任何操作 */ public void release() { if(!this.started ) // throw new RollbackException("事务没有开始"); return ; if(this.committed ) // throw new RollbackException("事务已经提交"); return ; if(this.rollbacked) // throw new RollbackException("事务已经回滚"); return; try { log.debug("Warnning:Detected uncommit Transaction,force rollbacked this transaction by system.",new Exception()); this._rollback(false); } catch (Throwable e) { // e.printStackTrace(); } } /** * 在final方法中调用,用来在出现异常时对事务资源进行回收,首先对事务进行回滚, * 然后回收资源,如果事务已经提交和回滚,则不做任何操作 */ public void releasenolog() { if(!this.started ) // throw new RollbackException("事务没有开始"); return ; if(this.committed ) // throw new RollbackException("事务已经提交"); return ; if(this.rollbacked) // throw new RollbackException("事务已经回滚"); return; try { // log.debug("Warnning:Detected uncommit Transaction,force rollbacked this transaction by system.",new Exception()); this._rollback(false); } catch (Throwable e) { // e.printStackTrace(); } } }