2
* Hibernate, Relational Persistence for Idiomatic Java
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.
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.
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
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
25
package org.hibernate.jdbc;
27
import java.io.Serializable;
28
import java.io.ObjectOutputStream;
29
import java.io.IOException;
30
import java.io.ObjectInputStream;
31
import java.sql.Connection;
32
import java.sql.SQLException;
34
import javax.transaction.TransactionManager;
36
import org.slf4j.Logger;
37
import org.slf4j.LoggerFactory;
38
import org.hibernate.ConnectionReleaseMode;
39
import org.hibernate.HibernateException;
40
import org.hibernate.Interceptor;
41
import org.hibernate.SessionException;
42
import org.hibernate.Transaction;
43
import org.hibernate.TransactionException;
44
import org.hibernate.util.JTAHelper;
45
import org.hibernate.engine.SessionFactoryImplementor;
46
import org.hibernate.exception.JDBCExceptionHelper;
47
import org.hibernate.transaction.CacheSynchronization;
48
import org.hibernate.transaction.TransactionFactory;
51
* Acts as the mediary between "entity-mode related" sessions in terms of
52
* their interaction with the JDBC data store.
54
* @author Steve Ebersole
56
public class JDBCContext implements Serializable, ConnectionManager.Callback {
58
// TODO : make this the factory for "entity mode related" sessions;
59
// also means making this the target of transaction-synch and the
60
// thing that knows how to cascade things between related sessions
62
// At that point, perhaps this thing is a "SessionContext", and
63
// ConnectionManager is a "JDBCContext"? A "SessionContext" should
64
// live in the impl package...
66
private static final Logger log = LoggerFactory.getLogger( JDBCContext.class );
68
public static interface Context extends TransactionFactory.Context {
70
* We cannot rely upon this method being called! It is only
71
* called if we are using Hibernate Transaction API.
73
public void afterTransactionBegin(Transaction tx);
74
public void beforeTransactionCompletion(Transaction tx);
75
public void afterTransactionCompletion(boolean success, Transaction tx);
76
public ConnectionReleaseMode getConnectionReleaseMode();
77
public boolean isAutoCloseSessionEnabled();
80
private Context owner;
81
private ConnectionManager connectionManager;
82
private transient boolean isTransactionCallbackRegistered;
83
private transient Transaction hibernateTransaction;
85
public JDBCContext(Context owner, Connection connection, Interceptor interceptor) {
87
this.connectionManager = new ConnectionManager(
90
owner.getConnectionReleaseMode(),
95
final boolean registerSynchronization = owner.isAutoCloseSessionEnabled()
96
|| owner.isFlushBeforeCompletionEnabled()
97
|| owner.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION;
98
if ( registerSynchronization ) {
99
registerSynchronizationIfPossible();
104
* Private constructor used exclusively for custom serialization...
107
private JDBCContext() {
110
// ConnectionManager.Callback implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112
public void connectionOpened() {
113
if ( owner.getFactory().getStatistics().isStatisticsEnabled() ) {
114
owner.getFactory().getStatisticsImplementor().connect();
118
public void connectionCleanedUp() {
119
if ( !isTransactionCallbackRegistered ) {
120
afterTransactionCompletion( false, null );
121
// Note : success = false, because we don't know the outcome of the transaction
125
public SessionFactoryImplementor getFactory() {
126
return owner.getFactory();
129
public ConnectionManager getConnectionManager() {
130
return connectionManager;
133
public Connection borrowConnection() {
134
return connectionManager.borrowConnection();
137
public Connection connection() throws HibernateException {
138
if ( owner.isClosed() ) {
139
throw new SessionException( "Session is closed" );
142
return connectionManager.getConnection();
145
public boolean registerCallbackIfNecessary() {
146
if ( isTransactionCallbackRegistered ) {
150
isTransactionCallbackRegistered = true;
156
public boolean registerSynchronizationIfPossible() {
157
if ( isTransactionCallbackRegistered ) {
158
// we already have a callback registered; either a local
159
// (org.hibernate.Transaction) transaction has accepted
160
// callback responsibilities, or we have previously
161
// registered a transaction synch.
164
boolean localCallbacksOnly = owner.getFactory().getSettings()
165
.getTransactionFactory()
166
.areCallbacksLocalToHibernateTransactions();
167
if ( localCallbacksOnly ) {
168
// the configured transaction-factory says it only supports
169
// local callback mode, so no sense attempting to register a
170
// JTA Synchronization
173
TransactionManager tm = owner.getFactory().getTransactionManager();
175
// if there is no TM configured, we will not be able to access
176
// the javax.transaction.Transaction object in order to
177
// register a synch anyway.
182
if ( !isTransactionInProgress() ) {
183
log.trace( "TransactionFactory reported no active transaction; Synchronization not registered" );
187
javax.transaction.Transaction tx = tm.getTransaction();
188
if ( JTAHelper.isMarkedForRollback( tx ) ) {
189
// transactions marked for rollback-only cause some TM impls to throw exceptions
190
log.debug( "Transaction is marked for rollback; skipping Synchronization registration" );
194
if ( hibernateTransaction == null ) {
195
hibernateTransaction = owner.getFactory().getSettings().getTransactionFactory().createTransaction( this, owner );
197
tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
198
isTransactionCallbackRegistered = true;
199
log.debug("successfully registered Synchronization");
204
catch( HibernateException e ) {
207
catch (Exception e) {
208
throw new TransactionException( "could not register synchronization with JTA TransactionManager", e );
213
public boolean isTransactionInProgress() {
214
return owner.getFactory().getSettings().getTransactionFactory()
215
.isTransactionInProgress( this, owner, hibernateTransaction );
218
public Transaction getTransaction() throws HibernateException {
219
if (hibernateTransaction==null) {
220
hibernateTransaction = owner.getFactory().getSettings()
221
.getTransactionFactory()
222
.createTransaction( this, owner );
224
return hibernateTransaction;
227
public void beforeTransactionCompletion(Transaction tx) {
228
log.trace( "before transaction completion" );
229
owner.beforeTransactionCompletion(tx);
233
* We cannot rely upon this method being called! It is only
234
* called if we are using Hibernate Transaction API.
236
public void afterTransactionBegin(Transaction tx) {
237
log.trace( "after transaction begin" );
238
owner.afterTransactionBegin(tx);
241
public void afterTransactionCompletion(boolean success, Transaction tx) {
242
log.trace( "after transaction completion" );
244
if ( getFactory().getStatistics().isStatisticsEnabled() ) {
245
getFactory().getStatisticsImplementor().endTransaction(success);
248
connectionManager.afterTransaction();
250
isTransactionCallbackRegistered = false;
251
hibernateTransaction = null;
252
owner.afterTransactionCompletion(success, tx);
256
* Called after executing a query outside the scope of
257
* a Hibernate or JTA transaction
259
public void afterNontransactionalQuery(boolean success) {
260
log.trace( "after autocommit" );
262
// check to see if the connection is in auto-commit
263
// mode (no connection means aggressive connection
264
// release outside a JTA transaction context, so MUST
265
// be autocommit mode)
266
boolean isAutocommit = connectionManager.isAutoCommit();
268
connectionManager.afterTransaction();
270
if ( isAutocommit ) {
271
owner.afterTransactionCompletion(success, null);
274
catch (SQLException sqle) {
275
throw JDBCExceptionHelper.convert(
276
owner.getFactory().getSQLExceptionConverter(),
278
"could not inspect JDBC autocommit mode"
284
// serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286
private void writeObject(ObjectOutputStream oos) throws IOException {
287
// isTransactionCallbackRegistered denotes whether any Hibernate
288
// Transaction has registered as a callback against this
289
// JDBCContext; only one such callback is allowed. Directly
290
// serializing this value causes problems with JDBCTransaction,
291
// or really any Transaction impl where the callback is local
292
// to the Transaction instance itself, since that Transaction
293
// is not serialized along with the JDBCContext. Thus we
294
// handle that fact here explicitly...
295
oos.defaultWriteObject();
296
boolean deserHasCallbackRegistered = isTransactionCallbackRegistered
297
&& ! owner.getFactory().getSettings().getTransactionFactory()
298
.areCallbacksLocalToHibernateTransactions();
299
oos.writeBoolean( deserHasCallbackRegistered );
302
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
303
ois.defaultReadObject();
304
isTransactionCallbackRegistered = ois.readBoolean();
308
* Custom serialization routine used during serialization of a
309
* Session/PersistenceContext for increased performance.
311
* @param oos The stream to which we should write the serial data.
312
* @throws IOException
314
public void serialize(ObjectOutputStream oos) throws IOException {
315
connectionManager.serialize( oos );
319
* Custom deserialization routine used during deserialization of a
320
* Session/PersistenceContext for increased performance.
322
* @param ois The stream from which to read the entry.
323
* @throws IOException
325
public static JDBCContext deserialize(
326
ObjectInputStream ois,
328
Interceptor interceptor) throws IOException {
329
JDBCContext jdbcContext = new JDBCContext();
330
jdbcContext.owner = context;
331
jdbcContext.connectionManager = ConnectionManager.deserialize(
333
context.getFactory(),
335
context.getConnectionReleaseMode(),