001: //$Id: JDBCTransaction.java 9595 2006-03-10 18:14:21Z steve.ebersole@jboss.com $
002: package org.hibernate.transaction;
003:
004: import java.sql.SQLException;
005: import java.util.ArrayList;
006: import java.util.List;
007:
008: import javax.transaction.Status;
009: import javax.transaction.Synchronization;
010:
011: import org.apache.commons.logging.Log;
012: import org.apache.commons.logging.LogFactory;
013:
014: import org.hibernate.HibernateException;
015: import org.hibernate.Transaction;
016: import org.hibernate.TransactionException;
017: import org.hibernate.jdbc.JDBCContext;
018:
019: /**
020: * Implements a basic transaction strategy for JDBC connections.This is the
021: * default <tt>Transaction</tt> implementation used if none is explicitly
022: * specified.
023: * @author Anton van Straaten, Gavin King
024: */
025: public class JDBCTransaction implements Transaction {
026:
027: private static final Log log = LogFactory
028: .getLog(JDBCTransaction.class);
029:
030: private final JDBCContext jdbcContext;
031: private final TransactionFactory.Context transactionContext;
032:
033: private boolean toggleAutoCommit;
034: private boolean begun;
035: private boolean rolledBack;
036: private boolean committed;
037: private boolean commitFailed;
038: private List synchronizations;
039: private boolean callback;
040: private int timeout = -1;
041:
042: public JDBCTransaction(JDBCContext jdbcContext,
043: TransactionFactory.Context transactionContext) {
044: this .jdbcContext = jdbcContext;
045: this .transactionContext = transactionContext;
046: }
047:
048: public void begin() throws HibernateException {
049: if (begun) {
050: return;
051: }
052: if (commitFailed) {
053: throw new TransactionException(
054: "cannot re-start transaction after failed commit");
055: }
056:
057: log.debug("begin");
058:
059: try {
060: toggleAutoCommit = jdbcContext.connection().getAutoCommit();
061: if (log.isDebugEnabled()) {
062: log.debug("current autocommit status: "
063: + toggleAutoCommit);
064: }
065: if (toggleAutoCommit) {
066: log.debug("disabling autocommit");
067: jdbcContext.connection().setAutoCommit(false);
068: }
069: } catch (SQLException e) {
070: log.error("JDBC begin failed", e);
071: throw new TransactionException("JDBC begin failed: ", e);
072: }
073:
074: callback = jdbcContext.registerCallbackIfNecessary();
075:
076: begun = true;
077: committed = false;
078: rolledBack = false;
079:
080: if (timeout > 0) {
081: jdbcContext.getConnectionManager().getBatcher()
082: .setTransactionTimeout(timeout);
083: }
084:
085: jdbcContext.afterTransactionBegin(this );
086: }
087:
088: private void closeIfRequired() throws HibernateException {
089: if (callback && transactionContext.shouldAutoClose()
090: && !transactionContext.isClosed()) {
091: try {
092: transactionContext.managedClose();
093: } catch (HibernateException he) {
094: log.error("Could not close session", he);
095: //swallow, the transaction was finished
096: }
097: }
098: }
099:
100: public void commit() throws HibernateException {
101: if (!begun) {
102: throw new TransactionException(
103: "Transaction not successfully started");
104: }
105:
106: log.debug("commit");
107:
108: if (!transactionContext.isFlushModeNever() && callback) {
109: transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
110: }
111:
112: notifyLocalSynchsBeforeTransactionCompletion();
113: if (callback) {
114: jdbcContext.beforeTransactionCompletion(this );
115: }
116:
117: try {
118: commitAndResetAutoCommit();
119: log.debug("committed JDBC Connection");
120: committed = true;
121: if (callback) {
122: jdbcContext.afterTransactionCompletion(true, this );
123: }
124: notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
125: } catch (SQLException e) {
126: log.error("JDBC commit failed", e);
127: commitFailed = true;
128: if (callback) {
129: jdbcContext.afterTransactionCompletion(false, this );
130: }
131: notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
132: throw new TransactionException("JDBC commit failed", e);
133: } finally {
134: closeIfRequired();
135: }
136: }
137:
138: private void commitAndResetAutoCommit() throws SQLException {
139: try {
140: jdbcContext.connection().commit();
141: } finally {
142: toggleAutoCommit();
143: }
144: }
145:
146: public void rollback() throws HibernateException {
147:
148: if (!begun && !commitFailed) {
149: throw new TransactionException(
150: "Transaction not successfully started");
151: }
152:
153: log.debug("rollback");
154:
155: if (!commitFailed) {
156:
157: /*notifyLocalSynchsBeforeTransactionCompletion();
158: if ( callback ) {
159: jdbcContext.notifyLocalSynchsBeforeTransactionCompletion( this );
160: }*/
161:
162: try {
163: rollbackAndResetAutoCommit();
164: log.debug("rolled back JDBC Connection");
165: rolledBack = true;
166: notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
167: } catch (SQLException e) {
168: log.error("JDBC rollback failed", e);
169: notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
170: throw new TransactionException("JDBC rollback failed",
171: e);
172: } finally {
173: if (callback) {
174: jdbcContext.afterTransactionCompletion(false, this );
175: }
176: closeIfRequired();
177: }
178: }
179: }
180:
181: private void rollbackAndResetAutoCommit() throws SQLException {
182: try {
183: jdbcContext.connection().rollback();
184: } finally {
185: toggleAutoCommit();
186: }
187: }
188:
189: private void toggleAutoCommit() {
190: try {
191: if (toggleAutoCommit) {
192: log.debug("re-enabling autocommit");
193: jdbcContext.connection().setAutoCommit(true);
194: }
195: } catch (Exception sqle) {
196: log.error("Could not toggle autocommit", sqle);
197: //swallow it (the transaction _was_ successful or successfully rolled back)
198: }
199: }
200:
201: public boolean wasRolledBack() {
202: return rolledBack;
203: }
204:
205: public boolean wasCommitted() {
206: return committed;
207: }
208:
209: public boolean isActive() {
210: return begun && !(rolledBack || committed | commitFailed);
211: }
212:
213: public void registerSynchronization(Synchronization sync)
214: throws HibernateException {
215: if (sync == null)
216: throw new NullPointerException("null Synchronization");
217: if (synchronizations == null) {
218: synchronizations = new ArrayList();
219: }
220: synchronizations.add(sync);
221: }
222:
223: private void notifyLocalSynchsBeforeTransactionCompletion() {
224: if (synchronizations != null) {
225: for (int i = 0; i < synchronizations.size(); i++) {
226: Synchronization sync = (Synchronization) synchronizations
227: .get(i);
228: try {
229: sync.beforeCompletion();
230: } catch (Throwable t) {
231: log.error("exception calling user Synchronization",
232: t);
233: }
234: }
235: }
236: }
237:
238: private void notifyLocalSynchsAfterTransactionCompletion(int status) {
239: begun = false;
240: if (synchronizations != null) {
241: for (int i = 0; i < synchronizations.size(); i++) {
242: Synchronization sync = (Synchronization) synchronizations
243: .get(i);
244: try {
245: sync.afterCompletion(status);
246: } catch (Throwable t) {
247: log.error("exception calling user Synchronization",
248: t);
249: }
250: }
251: }
252: }
253:
254: public void setTimeout(int seconds) {
255: timeout = seconds;
256: }
257: }
|