1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.transaction;
26
27 import java.sql.SQLException;
28 import java.util.ArrayList;
29 import java.util.List;
30 import javax.transaction.Status;
31 import javax.transaction.Synchronization;
32
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import org.hibernate.HibernateException;
37 import org.hibernate.Transaction;
38 import org.hibernate.TransactionException;
39 import org.hibernate.jdbc.JDBCContext;
40
41 /**
42 * {@link Transaction} implementation based on transaction management through
43 * a JDBC {@link java.sql.Connection}.
44 * <p/>
45 * This the Hibernate's default transaction strategy.
46 *
47 * @author Anton van Straaten
48 * @author Gavin King
49 */
50 public class JDBCTransaction implements Transaction {
51
52 private static final Logger log = LoggerFactory.getLogger(JDBCTransaction.class);
53
54 private final JDBCContext jdbcContext;
55 private final TransactionFactory.Context transactionContext;
56
57 private boolean toggleAutoCommit;
58 private boolean begun;
59 private boolean rolledBack;
60 private boolean committed;
61 private boolean commitFailed;
62 private List synchronizations;
63 private boolean callback;
64 private int timeout = -1;
65
66 public JDBCTransaction(JDBCContext jdbcContext, TransactionFactory.Context transactionContext) {
67 this.jdbcContext = jdbcContext;
68 this.transactionContext = transactionContext;
69 }
70
71 /**
72 * {@inheritDoc}
73 */
74 public void begin() throws HibernateException {
75 if (begun) {
76 return;
77 }
78 if (commitFailed) {
79 throw new TransactionException("cannot re-start transaction after failed commit");
80 }
81
82 log.debug("begin");
83
84 try {
85 toggleAutoCommit = jdbcContext.connection().getAutoCommit();
86 if ( log.isDebugEnabled() ) {
87 log.debug("current autocommit status: " + toggleAutoCommit);
88 }
89 if (toggleAutoCommit) {
90 log.debug("disabling autocommit");
91 jdbcContext.connection().setAutoCommit(false);
92 }
93 }
94 catch (SQLException e) {
95 log.error("JDBC begin failed", e);
96 throw new TransactionException("JDBC begin failed: ", e);
97 }
98
99 callback = jdbcContext.registerCallbackIfNecessary();
100
101 begun = true;
102 committed = false;
103 rolledBack = false;
104
105 if ( timeout>0 ) {
106 jdbcContext.getConnectionManager()
107 .getBatcher()
108 .setTransactionTimeout(timeout);
109 }
110
111 jdbcContext.afterTransactionBegin(this);
112 }
113
114 private void closeIfRequired() throws HibernateException {
115 if ( callback && transactionContext.shouldAutoClose() && !transactionContext.isClosed() ) {
116 try {
117 transactionContext.managedClose();
118 }
119 catch (HibernateException he) {
120 log.error("Could not close session", he);
121 //swallow, the transaction was finished
122 }
123 }
124 }
125
126 /**
127 * {@inheritDoc}
128 */
129 public void commit() throws HibernateException {
130 if (!begun) {
131 throw new TransactionException("Transaction not successfully started");
132 }
133
134 log.debug("commit");
135
136 if ( !transactionContext.isFlushModeNever() && callback ) {
137 transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
138 }
139
140 notifyLocalSynchsBeforeTransactionCompletion();
141 if ( callback ) {
142 jdbcContext.beforeTransactionCompletion( this );
143 }
144
145 try {
146 commitAndResetAutoCommit();
147 log.debug("committed JDBC Connection");
148 committed = true;
149 if ( callback ) {
150 jdbcContext.afterTransactionCompletion( true, this );
151 }
152 notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
153 }
154 catch (SQLException e) {
155 log.error("JDBC commit failed", e);
156 commitFailed = true;
157 if ( callback ) {
158 jdbcContext.afterTransactionCompletion( false, this );
159 }
160 notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
161 throw new TransactionException("JDBC commit failed", e);
162 }
163 finally {
164 closeIfRequired();
165 }
166 }
167
168 private void commitAndResetAutoCommit() throws SQLException {
169 try {
170 jdbcContext.connection().commit();
171 }
172 finally {
173 toggleAutoCommit();
174 }
175 }
176
177 /**
178 * {@inheritDoc}
179 */
180 public void rollback() throws HibernateException {
181
182 if (!begun && !commitFailed) {
183 throw new TransactionException("Transaction not successfully started");
184 }
185
186 log.debug("rollback");
187
188 if (!commitFailed) {
189
190 /*notifyLocalSynchsBeforeTransactionCompletion();
191 if ( callback ) {
192 jdbcContext.notifyLocalSynchsBeforeTransactionCompletion( this );
193 }*/
194
195 try {
196 rollbackAndResetAutoCommit();
197 log.debug("rolled back JDBC Connection");
198 rolledBack = true;
199 notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
200 }
201 catch (SQLException e) {
202 log.error("JDBC rollback failed", e);
203 notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
204 throw new TransactionException("JDBC rollback failed", e);
205 }
206 finally {
207 if ( callback ) {
208 jdbcContext.afterTransactionCompletion( false, this );
209 }
210 closeIfRequired();
211 }
212 }
213 }
214
215 private void rollbackAndResetAutoCommit() throws SQLException {
216 try {
217 jdbcContext.connection().rollback();
218 }
219 finally {
220 toggleAutoCommit();
221 }
222 }
223
224 private void toggleAutoCommit() {
225 try {
226 if (toggleAutoCommit) {
227 log.debug("re-enabling autocommit");
228 jdbcContext.connection().setAutoCommit( true );
229 }
230 }
231 catch (Exception sqle) {
232 log.error("Could not toggle autocommit", sqle);
233 //swallow it (the transaction _was_ successful or successfully rolled back)
234 }
235 }
236
237 /**
238 * {@inheritDoc}
239 */
240 public boolean wasRolledBack() {
241 return rolledBack;
242 }
243
244 /**
245 * {@inheritDoc}
246 */
247 public boolean wasCommitted() {
248 return committed;
249 }
250
251 /**
252 * {@inheritDoc}
253 */
254 public boolean isActive() {
255 return begun && ! ( rolledBack || committed | commitFailed );
256 }
257
258 /**
259 * {@inheritDoc}
260 */
261 public void registerSynchronization(Synchronization sync) throws HibernateException {
262 if (sync==null) throw new NullPointerException("null Synchronization");
263 if (synchronizations==null) {
264 synchronizations = new ArrayList();
265 }
266 synchronizations.add(sync);
267 }
268
269 private void notifyLocalSynchsBeforeTransactionCompletion() {
270 if (synchronizations!=null) {
271 for ( int i=0; i<synchronizations.size(); i++ ) {
272 Synchronization sync = (Synchronization) synchronizations.get(i);
273 try {
274 sync.beforeCompletion();
275 }
276 catch (Throwable t) {
277 log.error("exception calling user Synchronization", t);
278 }
279 }
280 }
281 }
282
283 private void notifyLocalSynchsAfterTransactionCompletion(int status) {
284 begun = false;
285 if (synchronizations!=null) {
286 for ( int i=0; i<synchronizations.size(); i++ ) {
287 Synchronization sync = (Synchronization) synchronizations.get(i);
288 try {
289 sync.afterCompletion(status);
290 }
291 catch (Throwable t) {
292 log.error("exception calling user Synchronization", t);
293 }
294 }
295 }
296 }
297
298 /**
299 * {@inheritDoc}
300 */
301 public void setTimeout(int seconds) {
302 timeout = seconds;
303 }
304 }