/* * 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; /** * <p>Title: </p> * * <p>Description: </p> * * <p>Copyright: Copyright (c) 2005</p> * * <p>Company: </p> * * @author biaoping.yin * @version 1.0 */ //$Id: JDBCTransaction.java,v 1.12 2005/05/03 21:50:17 oneovthafew Exp $ import java.sql.Connection; import java.sql.SQLException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Stack; import javax.sql.DataSource; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.xa.XAResource; import com.frameworkset.common.poolman.NestedSQLException; import com.frameworkset.common.poolman.util.SQLManager; import com.frameworkset.orm.annotation.TransactionType; /** * Implements a basic transaction strategy for JDBC connections.This is the * default <tt>Transaction</tt> implementation used if none is explicitly * specified. * @since 2007.05.16 * @author biaoping.yin */ public class JDBCTransaction { /** * 事务类型,默认为无事务 */ private Map<Object,TransactionEntity> txentities = new HashMap<Object,TransactionEntity>(1); private Stack<TransactionEntity> executeStack = new Stack<TransactionEntity>(); private int count = 0; TransactionType currenttxtype; public TransactionType getTXType() { return this.currenttxtype; } JDBCTransaction parent_tx = null; private int status = Status.STATUS_UNKNOWN; JDBCTransaction(TransactionType currenttxtype) { this.currenttxtype = currenttxtype; } JDBCTransaction(JDBCTransaction parent_tx,TransactionType currenttxtype) { this.parent_tx = parent_tx; this.currenttxtype = currenttxtype; } public Connection getConnection() throws TransactionException { return this.getConnection(null); } public void printStackTrace() { if(this.txentities != null && this.txentities.size() > 0) { Iterator<Map.Entry<Object, TransactionEntity>> entries = txentities.entrySet().iterator(); while(entries.hasNext()) { Map.Entry<Object, TransactionEntity> entry = entries.next(); if(entry.getValue() != null) entry.getValue().printStackTrace(); } } } class TransactionEntity { // super. Connection con; private Exception createBy; private int status = Status.STATUS_UNKNOWN; private boolean toggleAutoCommit = true; private int count; boolean errorflag = false; private static final String info = "Transaction Entity created on "; TransactionEntity(Connection originecon) throws SQLException { // this.con = new InnerConnection(originecon); this.con = originecon; createBy = new Exception(info + new Date()); try { toggleAutoCommit = con.getAutoCommit(); } catch(SQLException e) { errorflag = true; throw e; } if(toggleAutoCommit && currenttxtype != TransactionType.RW_TRANSACTION) { con.setAutoCommit(false); } } public void printStackTrace() { createBy.printStackTrace(); } protected void increament() { this.count ++; } //计数器减一,目前没有调用 protected void decreament() { this.count --; if(count == 0) this.status = Status.STATUS_COMMITTING; } protected int getStatus() { return this.status; } protected void setStatus(int status) { this.status = status; } protected void commit() throws SQLException { try { if(con != null && currenttxtype != TransactionType.RW_TRANSACTION) this.con.commit(); // this.status = Status.STATUS_COMMITTED; } catch (SQLException e) { // TODO Auto-generated catch block // this.status = Status.STATUS_COMMITTED; throw e; } catch (Exception e) { // TODO Auto-generated catch block // this.status = Status.STATUS_COMMITTED; throw new NestedSQLException(e.getMessage(),e); } finally { try { if(con != null && currenttxtype != TransactionType.RW_TRANSACTION) this.con.setAutoCommit(this.toggleAutoCommit); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if(con != null) this.con.close(); } catch(Exception e) { } con = null; } this.status = Status.STATUS_COMMITTED; } protected void rollback() { try { this.status = Status.STATUS_ROLLING_BACK; if(con != null && currenttxtype != TransactionType.RW_TRANSACTION) this.con.rollback(); this.status = Status.STATUS_ROLLEDBACK; } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { this.count = 0; try { if(con != null && currenttxtype != TransactionType.RW_TRANSACTION) this.con.setAutoCommit(this.toggleAutoCommit); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if(con != null) this.con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } con = null; this.status = Status.STATUS_ROLLEDBACK; } } protected void destroy() { if(this.con != null) { if(!this.errorflag) { try { if(currenttxtype != TransactionType.RW_TRANSACTION) { this.con.setAutoCommit(this.toggleAutoCommit); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { if(!con.isClosed()) { con.close(); } } catch(Exception e) { } } } } protected Connection getConnection() { // TODO Auto-generated method stub return this.con; } } protected void begin() throws TransactionException { if(wasRolledBack()) { throw new TransactionException("JDBCTransaction.begin() failed:TX has been RollBacked."); } if(this.wasCommitted()) throw new TransactionException("JDBCTransaction.begin() failed:TX has been commited."); if(this.status == Status.STATUS_MARKED_ROLLBACK) throw new TransactionException("JDBCTransaction.begin() failed:TX has been MARKED ROLLBACK."); increament(); } protected void increament() { this.count ++; } public Connection getConnectionFromDS(DataSource datasource) throws TransactionException { if(wasRolledBack()) { throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS():TX was RolledBack.")); } if(this.wasCommitted()) throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:TX was committed.")); //注释:对应RW事务,然后允许进行相应的数据库操作,保留修改痕迹而注释 // if(this.status == Status.STATUS_MARKED_ROLLBACK) // { // throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:TX was remarked rollingback.")); // } if(this.currenttxtype != TransactionManager.RW_TRANSACTION && this.status == Status.STATUS_MARKED_ROLLBACK) { throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:TX was remarked rollingback.")); } TransactionEntity txentity = null; // String t_dbName = (dbName == null ?SQLManager.getInstance().getDefaultDBName():dbName); try { // String t_dbName = (dbName == null ?"NULL":dbName); // t_dbName = SQLManager.getRealDBNameFromExternalDBName(t_dbName); txentity = txentities.get(datasource); if(this.status != Status.STATUS_ACTIVE) this.status = Status.STATUS_ACTIVE; Connection con = null; if(txentity == null) { con = datasource.getConnection(); if(con == null) throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:Request connection from datasource return null!")); txentity = new TransactionEntity(con); txentity.increament(); txentity.setStatus(Status.STATUS_ACTIVE); txentities.put(datasource,txentity); executeStack.push(txentity); } else { if(txentity.getStatus() == Status.STATUS_COMMITTED || txentity.getStatus() == Status.STATUS_ROLLEDBACK || txentity.getStatus() == Status.STATUS_ROLLING_BACK) throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:" + "TransactionEntity.getStatus() == Status.STATUS_COMMITTED " + "|| TransactionEntity.getStatus() == Status.STATUS_MARKED_ROLLBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLEDBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLING_BACK")); else if(this.currenttxtype != TransactionManager.RW_TRANSACTION && txentity.getStatus() == Status.STATUS_MARKED_ROLLBACK) { throw new TransactionException(new SQLException("JDBCTransaction.getConnectionFromDS:" + "TransactionEntity.getStatus() == Status.STATUS_COMMITTED " + "|| TransactionEntity.getStatus() == Status.STATUS_MARKED_ROLLBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLEDBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLING_BACK")); } txentity.increament(); } return txentity.getConnection(); } catch(Throwable e) { if(txentity != null) { txentity.destroy(); try { this.executeStack.removeElement(txentity); } catch(Exception ie) { ie.printStackTrace(); } try { this.txentities.remove(datasource); } catch(Exception ie) { ie.printStackTrace(); } } throw new TransactionException(e); } } public Connection getConnection(String dbName) throws TransactionException { if(wasRolledBack()) { throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):TX was RolledBack.")); } if(this.wasCommitted()) throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):TX was committed.")); if(this.currenttxtype != TransactionManager.RW_TRANSACTION && this.status == Status.STATUS_MARKED_ROLLBACK) { throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):TX was remarked rollingback.")); } TransactionEntity txentity = null; String t_dbName = (dbName == null ?SQLManager.getInstance().getDefaultDBName():dbName); try { // String t_dbName = (dbName == null ?"NULL":dbName); t_dbName = SQLManager.getRealDBNameFromExternalDBName(t_dbName); txentity = txentities.get(t_dbName); if(this.status != Status.STATUS_ACTIVE) this.status = Status.STATUS_ACTIVE; Connection con = null; if(txentity == null) { con = SQLManager.getInstance().requestConnection(t_dbName); if(con == null) throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):Request connection from pool return null!")); txentity = new TransactionEntity(con); txentity.increament(); txentity.setStatus(Status.STATUS_ACTIVE); txentities.put(t_dbName,txentity); executeStack.push(txentity); } else { if(txentity.getStatus() == Status.STATUS_COMMITTED || txentity.getStatus() == Status.STATUS_ROLLEDBACK || txentity.getStatus() == Status.STATUS_ROLLING_BACK) throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):" + "TransactionEntity.getStatus() == Status.STATUS_COMMITTED " + "|| TransactionEntity.getStatus() == Status.STATUS_MARKED_ROLLBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLEDBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLING_BACK")); else if(this.currenttxtype != TransactionManager.RW_TRANSACTION && txentity.getStatus() == Status.STATUS_MARKED_ROLLBACK) { throw new TransactionException(new SQLException("JDBCTransaction.getConnection(dbName="+ dbName +"):" + "TransactionEntity.getStatus() == Status.STATUS_COMMITTED " + "|| TransactionEntity.getStatus() == Status.STATUS_MARKED_ROLLBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLEDBACK" + "|| TransactionEntity.getStatus() == Status.STATUS_ROLLING_BACK")); } txentity.increament(); } return txentity.getConnection(); } catch(Exception e) { if(txentity != null) { txentity.destroy(); try { this.executeStack.removeElement(txentity); } catch(Exception ie) { ie.printStackTrace(); } try { this.txentities.remove(t_dbName); } catch(Exception ie) { ie.printStackTrace(); } } throw new TransactionException(e); } } public void closeIfRequired() throws TransactionException { } // public void commit() { // try { // this.con.commit(); // this.con.setAutoCommit(this.toggleAutoCommit); // } catch (SQLException e) { // throw new TransactionException(e); // } // } public void commitAndResetAutoCommit() throws SQLException { } // public void rollback() throws TransactionException { // try // { // this.con.rollback(); // this.con.setAutoCommit(this.toggleAutoCommit); // } // catch(Exception e) // { // throw new TransactionException(e); // } // } public void rollbackAndResetAutoCommit() throws SQLException { } public void toggleAutoCommit() { } public boolean wasRolledBack() { return this.status == Status.STATUS_ROLLEDBACK ; } public boolean wasCommitted() { return this.status == Status.STATUS_COMMITTED ; } public boolean isActive() { return false; } // public void registerSynchronization(Synchronization sync) throws // TransactionException { // } public void beforeTransactionCompletion() { } public void afterTransactionCompletion(int status) { } public void createTransaction() { } public boolean delistResource(XAResource arg0, int arg1) throws IllegalStateException, SystemException { // TODO Auto-generated method stub return false; } public boolean enlistResource(XAResource arg0) throws RollbackException, IllegalStateException, SystemException { return false; } public int getStatus() { return this.status; } public void setRollbackOnly() throws IllegalStateException, SystemException { this.status = Status.STATUS_MARKED_ROLLBACK; // this.rollback(); } protected void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { this.commit(null); } private void updataStatus(int status) { this.status = status; } /** * 事务引用计数器减一 */ public void decreament() { this.count --; } protected void commit(String dbName) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { if(this.status == Status.STATUS_MARKED_ROLLBACK) { System.out.println("标记为待回滚的事务"); // this.rollback(); throw new RollbackException("Commit " + dbName + " TX failed: TX MARKED_ROLLBACK" ); } decreament(); if(count <= 0) { this.status = Status.STATUS_PREPARING; try { this.commitAll(); this.status = Status.STATUS_COMMITTED; } catch (SQLException e) { // this.status = Status.STATUS_MARKED_ROLLBACK; try { this.setRollbackOnly(); } catch (SystemException se) { // TODO Auto-generated catch block se.printStackTrace(); } this.increament(); //重新加1防止在回滚时重复减1 throw new RollbackException(e.getMessage()); } } } private void commitAll() throws SQLException { while (executeStack.size() > 0 ) { TransactionEntity txentity = this.executeStack.pop() ; if(txentity == null) break; else { txentity.commit(); } } this.txentities.clear(); } private void rollbackAll() { /** * 每次显示地调用回滚操作后,立即释放所有的资源,回滚所有的数据库事务 */ while (executeStack.size() > 0) { TransactionEntity txentity = this.executeStack.pop() ; if(txentity == null) break; else { txentity.rollback(); } } this.txentities.clear(); } public void registerSynchronization(Synchronization arg0) throws RollbackException, IllegalStateException, SystemException { } protected void rollback() throws IllegalStateException, SystemException { this.rollback(null); } /** * 一个回滚全部回滚 * @param dbName * @throws IllegalStateException */ protected void rollback(String dbName) throws IllegalStateException { if(this.status == Status.STATUS_ROLLEDBACK) { // if(executeStack.size() > 0) // { // System.out.println("executeStack "); // } throw new IllegalStateException("事务已经回滚"); } if(this.status == Status.STATUS_COMMITTED) { // if(executeStack.size() > 0) // { // System.out.println("executeStack"); // } throw new IllegalStateException("事务已经提交"); } /** * 计数器减一 */ this.decreament(); if(this.count <= 0) { this.rollbackAll(); this.status = Status.STATUS_ROLLEDBACK; } else { // this.status = Status.STATUS_MARKED_ROLLBACK; try { this.setRollbackOnly(); } catch (SystemException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(this.currenttxtype != TransactionManager.RW_TRANSACTION) { this.rollbackAll(); } } } protected JDBCTransaction getParent_tx() { return parent_tx; } }