Java tutorial
/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA * */ package org.hibernate.transaction; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.Transaction; import org.hibernate.TransactionException; import org.hibernate.jdbc.JDBCContext; import org.hibernate.util.JTAHelper; /** * {@link Transaction} implementation based on transaction management through * a JTA {@link UserTransaction}. Similar to {@link CMTTransaction}, except * here we are actually managing the transactions through the Hibernate * transaction mechanism. * * @author Gavin King * @author Steve Ebersole * @author Les Hazlewood */ public class JTATransaction implements Transaction { private static final Log log = LogFactory.getLog(JTATransaction.class); private final JDBCContext jdbcContext; private final TransactionFactory.Context transactionContext; private UserTransaction userTransaction; private boolean newTransaction; private boolean begun; private boolean commitFailed; private boolean commitSucceeded; private boolean callback; public JTATransaction(UserTransaction userTransaction, JDBCContext jdbcContext, TransactionFactory.Context transactionContext) { this.jdbcContext = jdbcContext; this.transactionContext = transactionContext; this.userTransaction = userTransaction; } public void begin() throws HibernateException { if (begun) { return; } if (commitFailed) { throw new TransactionException("cannot re-start transaction after failed commit"); } log.debug("begin"); try { newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION; if (newTransaction) { userTransaction.begin(); log.debug("Began a new JTA transaction"); } } catch (Exception e) { log.error("JTA transaction begin failed", e); throw new TransactionException("JTA transaction begin failed", e); } /*if (newTransaction) { // don't need a synchronization since we are committing // or rolling back the transaction ourselves - assuming // that we do no work in beforeTransactionCompletion() synchronization = false; }*/ boolean synchronization = jdbcContext.registerSynchronizationIfPossible(); if (!newTransaction && !synchronization) { log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled"); } if (!synchronization) { //if we could not register a synchronization, //do the before/after completion callbacks //ourself (but we need to let jdbcContext //know that this is what we are going to //do, so it doesn't keep trying to register //synchronizations) callback = jdbcContext.registerCallbackIfNecessary(); } begun = true; commitSucceeded = false; jdbcContext.afterTransactionBegin(this); } public void commit() throws HibernateException { if (!begun) { throw new TransactionException("Transaction not successfully started"); } log.debug("commit"); boolean flush = !transactionContext.isFlushModeNever() && (callback || !transactionContext.isFlushBeforeCompletionEnabled()); if (flush) { transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback() } if (callback && newTransaction) { jdbcContext.beforeTransactionCompletion(this); } closeIfRequired(); if (newTransaction) { try { userTransaction.commit(); commitSucceeded = true; log.debug("Committed JTA UserTransaction"); } catch (Exception e) { commitFailed = true; // so the transaction is already rolled back, by JTA spec log.error("JTA commit failed", e); throw new TransactionException("JTA commit failed: ", e); } finally { afterCommitRollback(); } } else { // this one only really needed for badly-behaved applications! // (if the TransactionManager has a Sychronization registered, // its a noop) // (actually we do need it for downgrading locks) afterCommitRollback(); } } public void rollback() throws HibernateException { if (!begun && !commitFailed) { throw new TransactionException("Transaction not successfully started"); } log.debug("rollback"); /*if (!synchronization && newTransaction && !commitFailed) { jdbcContext.beforeTransactionCompletion(this); }*/ try { closeIfRequired(); } catch (Exception e) { log.error("could not close session during rollback", e); //swallow it, and continue to roll back JTA transaction } try { if (newTransaction) { if (!commitFailed) { userTransaction.rollback(); log.debug("Rolled back JTA UserTransaction"); } } else { userTransaction.setRollbackOnly(); log.debug("set JTA UserTransaction to rollback only"); } } catch (Exception e) { log.error("JTA rollback failed", e); throw new TransactionException("JTA rollback failed", e); } finally { afterCommitRollback(); } } private static final int NULL = Integer.MIN_VALUE; private void afterCommitRollback() throws TransactionException { begun = false; if (callback) { // this method is a noop if there is a Synchronization! if (!newTransaction) { log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled"); } int status = NULL; try { status = userTransaction.getStatus(); } catch (Exception e) { log.error("Could not determine transaction status after commit", e); throw new TransactionException("Could not determine transaction status after commit", e); } finally { /*if (status!=Status.STATUS_COMMITTED && status!=Status.STATUS_ROLLEDBACK) { log.warn("Transaction not complete - you should set hibernate.transaction.manager_lookup_class if cache is enabled"); //throw exception?? }*/ jdbcContext.afterTransactionCompletion(status == Status.STATUS_COMMITTED, this); } } } public boolean wasRolledBack() throws TransactionException { //if (!begun) return false; //if (commitFailed) return true; final int status; try { status = userTransaction.getStatus(); } catch (SystemException se) { log.error("Could not determine transaction status", se); throw new TransactionException("Could not determine transaction status", se); } if (status == Status.STATUS_UNKNOWN) { throw new TransactionException("Could not determine transaction status"); } else { return JTAHelper.isRollback(status); } } public boolean wasCommitted() throws TransactionException { //if (!begun || commitFailed) return false; final int status; try { status = userTransaction.getStatus(); } catch (SystemException se) { log.error("Could not determine transaction status", se); throw new TransactionException("Could not determine transaction status: ", se); } if (status == Status.STATUS_UNKNOWN) { throw new TransactionException("Could not determine transaction status"); } else { return status == Status.STATUS_COMMITTED; } } public boolean isActive() throws TransactionException { if (!begun || commitFailed || commitSucceeded) { return false; } final int status; try { status = userTransaction.getStatus(); } catch (SystemException se) { log.error("Could not determine transaction status", se); throw new TransactionException("Could not determine transaction status: ", se); } if (status == Status.STATUS_UNKNOWN) { throw new TransactionException("Could not determine transaction status"); } else { return status == Status.STATUS_ACTIVE; } } public void registerSynchronization(Synchronization sync) throws HibernateException { if (getTransactionManager() == null) { throw new IllegalStateException("JTA TransactionManager not available"); } else { try { getTransactionManager().getTransaction().registerSynchronization(sync); } catch (Exception e) { throw new TransactionException("could not register synchronization", e); } } } private TransactionManager getTransactionManager() { return transactionContext.getFactory().getTransactionManager(); } private void closeIfRequired() throws HibernateException { boolean close = callback && transactionContext.shouldAutoClose() && !transactionContext.isClosed(); if (close) { transactionContext.managedClose(); } } public void setTimeout(int seconds) { try { userTransaction.setTransactionTimeout(seconds); } catch (SystemException se) { throw new TransactionException("could not set transaction timeout", se); } } protected UserTransaction getUserTransaction() { return userTransaction; } }