Hibernate.orgCommunity Documentation
It is important to understand that the term transaction has many related, yet different meanings in regards to persistence and Object/Relational Mapping. In most use cases these definitions align, but that is not always the case.
It might refer to the physical transaction with the database.
It might refer to the logical notion of a transaction as related to a persistence context.
It might refer to the application notion of a Unit-of-Work, as defined by the archetypal pattern.
This documentation largely treats the physical and logic notions of transaction as one-in-the-same.
Hibernate uses the JDBC API for persistence. In the world of Java there are 2 well defined mechanism for dealing with transactions in JDBC: JDBC, itself, and JTA. Hibernate supports both mechanisms for integrating with transactions and allowing applications to manage physical transactions.
The first concept in understanding Hibernate transaction support is the
org.hibernate.engine.transaction.spi.TransactionFactory
interface which
serves 2 main functions:
It allows Hibernate to understand the transaction semantics of the environment. Are we operating in a JTA environment? Is a physical transaction already currently active? etc.
It acts as a factory for org.hibernate.Transaction
instances which
are used to allow applications to manage and check the state of transactions.
org.hibernate.Transaction
is Hibernate's notion of a logical
transaction. JPA has a similar notion in the
javax.persistence.EntityTransaction
interface.
org.hibernate.engine.transaction.spi.TransactionFactory
is a standard
Hibernate service. See Section 7.5.16, “org.hibernate.engine.transaction.spi.TransactionFactory
” for details.
JDBC-based transaction management leverages the JDBC defined methods
java.sql.Connection.commit()
and
java.sql.Connection.rollback()
(JDBC does not define and explicit
method of beginning a transaction). In Hibernate, this approach is represented by the
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
class.
JTA-based transaction management leverages the JTA
javax.transaction.TransactionManager
interface as obtained from
org.hibernate.service.jta.platform.spi.JtaPlatform
API. This approach
is represented by the
org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
class.
See Section 7.5.9, “org.hibernate.service.jta.platform.spi.JtaPlatform
” for information on integration with the underlying JTA
system.
CMT-based transaction management leverages the
javax.transaction.UserTransaction
interface as obtained from
org.hibernate.service.jta.platform.spi.JtaPlatform
API. This approach
is represented by the
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
class.
See Section 7.5.9, “org.hibernate.service.jta.platform.spi.JtaPlatform
” for information on integration with the underlying JTA
system.
Its is also possible to plug in ones own transaction approach by implementing the
org.hibernate.engine.transaction.spi.TransactionFactory
contract.
The default service initiator has built-in support for understanding custom transaction approaches
via the hibernate.transaction.factory_class
which can name either:
The instance of org.hibernate.engine.transaction.spi.TransactionFactory
to use.
The name of a class implementing
org.hibernate.engine.transaction.spi.TransactionFactory
to use. The expectation is that the implementation class have a no-argument constructor.
During development of 4.0, most of these classes named here were moved to new packages. To help facilitate upgrading, Hibernate will also recognize the legacy names here for a short period of time.
org.hibernate.transaction.JDBCTransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
org.hibernate.transaction.JTATransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory
org.hibernate.transaction.CMTTransactionFactory
is mapped to
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
Hibernate uses JDBC connections and JTA resources directly, without adding any additional locking behavior. It is important for you to become familiar with the JDBC, ANSI SQL, and transaction isolation specifics of your database management system.
Hibernate does not lock objects in memory. The behavior defined by the isolation level of your database
transactions does not change when you use Hibernate. The Hibernate
org.hibernate.Session
acts as a transaction-scoped cache providing
repeatable reads for lookup by identifier and entity queries and not-reporting queries that return scalar
values.
To reduce lock contention in the database, a database transaction needs to be as short as possible. Long
database transactions prevent your application from scaling to a highly-concurrent load. Do not hold a
database transaction open during end-user-level work, but open it after the end-user-level work is finished.
This is concept is referred to as transactional write-behind
.
Session-per-operation refers to the anti-pattern of opening and closing a
Session
for each database call in a single thread. It is also an anti-pattern in terms of
database transactions. Group your database calls into a planned sequence. In the same way, do not auto-commit
after every SQL statement in your application. Hibernate disables, or expects the application server to disable,
auto-commit mode immediately. Database transactions are never optional. All communication with a database must
be encapsulated by a transaction. Avoid auto-commit behavior for reading data, because many small transactions
are unlikely to perform better than one clearly-defined unit of work, and are more difficult to maintain and
extend.
For more anti-patterns to avoid, see Section 2.4.5, “Problems and anti-patterns”.
The most common pattern in a multi-user client/server application is session-per-request. In this model, a client sends a request to the server, where the Hibernate persistence layer is running. Hibernate opens a new Session, and all database operations are executed in this unit of work. After the work is completed, and the server prepares the response for the client, the session is flushed and closed. Use a single database transaction to serve the clients request, starting and committing it when you open and close the Session. The relationship between the transaction and the Session is one-to-one.
Hibernate provides built-in management of the current session to simplify the session-per-request pattern. Start a transaction to process a server request, and end the transaction before sending the response to the client. Common solutions include:
ServletFilter
AOP interceptor with a pointcut on the service methods
A proxy/interception container
An EJB container is a standardized way to implement cross-cutting aspects such as transaction demarcation on EJB session beans in a declarative manner with CMT. If you prefer programmatic transaction demarcation, see Section 2.5, “Hibernate Transaction API (JTA)”.
To access a current session to process the request, call method
sessionFactory.getCurrentSession()
. The returned Session
is
scoped to the current database transaction. You need to configure this for either resource-local or JTA
environments
You can extend the scope of a Session
and database transaction until the view is
rendered. This is especially useful in servlet applications that include a separate rendering phase after the
request is processed. To extending the database transaction until view rendering, implement your own
interceptor. This implementation is difficult if you rely on EJBs with container-managed transactions. A
transaction is completed when an EJB method returns, before rendering of any view can start. Search the
Hibernate forums for tips and examples relating to this Open Session in View pattern.
If session-per-request does not seem well-suited for your application, refer to Section 2.4.3, “Conversations” for another choice.
The session-per-request pattern is not the only way of designing units of work. Many business processes require a whole series of interactions with the user that are interleaved with database accesses. In web and enterprise applications, it is not acceptable for a database transaction to span a user interaction. Consider the following example:
Procedure 2.1. An example of a long-running conversation
The first screen of a dialog opens. The data seen by the user is loaded in a particular
Session
and database transaction. The user is free to modify the objects.
The user uses a UI element to save their work after five minutes of editing. The modifications are made persistent. The user also expects to have exclusive access to the data during the edit session.
From the point of view of the user, this unit of work is a long-running conversation or application transaction. There are many ways to implement this in your application.
A first naive implementation might keep the Session
and database transaction open while the
user is editing, using database-level locks to prevent other users from modifying the same data and to guarantee
isolation and atomicity. This is an anti-pattern, because lock contention is a bottleneck which will prevent
scalability in the future.
Several database transactions are used to implement the conversation. In this case, maintaining isolation of business processes becomes the partial responsibility of the application tier. A single conversation usually spans several database transactions. It is atomic if only one of these database transactions, typically the last one, stores the updated data. All others only read data. A common way to receive this data is through a wizard-style dialog spanning several request/response cycles. Hibernate includes some features which make this easy to implement.
Automatic Versioning |
Hibernate can perform automatic optimistic concurrency control for you. It can automatically detect if a concurrent modification occurred during user think time. Check for this at the end of the conversation. |
Detached Objects |
If you decide to use the session-per-request pattern, all loaded instances will be in the detached state during user think time. Hibernate allows you to reattach the objects and persist the modifications. The pattern is called session-per-request-with-detached-objects. Automatic versioning is used to isolate concurrent modifications. |
Extended (or Long) Session |
Extended (or Long) Session: the Hibernate Session can be disconnected from the underlying JDBC connection after the database transaction has been committed and reconnected when a new client request occurs. This pattern is known as session-per-conversation and makes even reattachment unnecessary. Automatic versioning is used to isolate concurrent modifications and the Session will not be allowed to be flushed automatically, but explicitly. |
Session-per-request-with-detached-objects and session-per-conversation each have advantages and disadvantages.
An application can concurrently access the same persistent state in two different Sessions. However, an instance
of a persistent class is never shared between two Session
instances. Therefore, two
different notions of identity exist: Database identity and JVM identity.
For objects attached to a particular Session, the two notions are equivalent, and JVM identity for database identity is guaranteed by Hibernate. The application might concurrently access a business object with the same identity in two different sessions, the two instances are actually different, in terms of JVM identity. Conflicts are resolved using an optimistic approach and automatic versioning at flush/commit time.
This approach places responsibility for concurrency on Hibernate and the database. It also provides the best
scalability, since expensive locking is not needed to guarantee identity in single-threaded units of work. The
application does not need to synchronize on any business object, as long as it maintains a single thread per
Session
. Within a Session
the application can safely use the
==
operator to compare objects.
However, an application that uses the ==
operator outside of a Session
may introduce problems.. If you put two detached instances into the same Set
, they might
use the same database identity, which means they represent the same row in the database. They would not be
guaranteed to have the same JVM identity if they are in a detached state. Override the
equals
and hashCode
methods in persistent classes, so that
they have their own notion of object equality. Never use the database identifier to implement equality. Instead,
use a business key that is a combination of unique, typically immutable, attributes. The database identifier
changes if a transient object is made persistent. If the transient instance, together with detached instances,
is held in a Set
, changing the hash-code breaks the contract of the
Set
. Attributes for business keys can be less stable than database primary keys. You only
need to guarantee stability as long as the objects are in the same Set
.This is not a
Hibernate issue, but relates to Java's implementation of object identity and equality.
Avoid the anti-patterns session-per-user-session or session-per-application, for the most part. The reasons are outlined in Section 2.4.1, “Session-per-operation”. In addition, keep the following points in mind when choosing patterns for your application.
Session
is not thread-safe.
Resources, such as HTTP requests, session beans, or Swing workers, cause race conditions if a
Session
instance is shared. If you keep your Hibernate
Session
in your HttpSession
, consider synchronizing access
to your Http
session. Otherwise, a reloaded application may use the same
Session
in two concurrently-running threads.
Session
immediately.In addition to the above, if your Session is bound to the application, you must stop the application. Rolling back the database transaction does not put your business objects back into their state at the start of the transaction. The database state and the business objects will be out of sync. Since exceptions are not recoverable, this is typically not a problem. You must start over after rollback anyway.
A persistent state refers to objects that are watched and checked for dirty state by Hibernate. If you keep the cache open for a long time or load too much data, it grows to fill available memory, causing an OutOfMemoryException.
To solve this problem, you can call clear()
and evict()
to manage the Session
cache. Alternately, you can use a Stored Procedure if you
need mass data operations. Keeping a Session
open for the duration of a user
session reduces the probability of stale data.
If your persistence layer runs in an application server, such as behind EJB session beans, every datasource connection obtained by Hibernate is automatically part of the global JTA transaction. You can also install a standalone JTA implementation and use it without EJB. Hibernate offers two strategies for JTA integration.
If you use bean-managed transactions (BMT), Hibernate tells the application server to start and end a BMT transaction if you use the Transaction API. The transaction management code is identical to the non-managed environment.
Example 2.3. BMT idiom
// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
Alternately, you can use a transaction-bound Session. If you need the
getCurrentSession()
functionality for easy context propagation, use the JTA
UserTransaction API directly.
Example 2.4. BMT idiom with getCurrentSession()
// BMT idiom with getCurrentSession()
try {
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
tx.begin();
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
tx.rollback();
throw e; // or display error message
}
With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner. This reduces the amount of code.
In a CMT/EJB, rollback happens automatically as well. An unhandled RuntimeException
thrown by a session bean method tells the container to set the global transaction to rollback. You do not need
to intervene, and you get automatic propagation of the Current
Session
bound to the transaction.
When configuring Hibernate's transaction factory, choose
org.hibernate.transaction.JTATransactionFactory
if you use JTA directly (BMT), and
org.hibernate.transaction.CMTTransactionFactory
in a CMT session bean. Set the
hibernate.transaction.manager_lookup_class
property as well. Ensure that your
hibernate.current_session_context_class
is either unset, or is set to
jta
.
The getCurrentSession()
method has one downside in a JTA environment. If you use it,
after_statement connection release mode is also used by default. Due to a limitation of the JTA specification,
Hibernate cannot automatically clean up any unclosed ScrollableResults
or
Iterator
instances returned by scroll()
or
iterate()
. Release the underlying database cursor by calling
ScrollableResults.close()
or Hibernate.close(Iterator)
explicitly from a finally
block. Try to avoid using scroll()
or iterate()
from the JTA or CMT code.
Copyright © 2011 Red Hat, Inc.