Hibernate.orgCommunity Documentation

Chapter 2. Transactions and concurrency control

2.1. Defining Transaction
2.2. Physical Transactions
2.2.1. Physical Transactions - JDBC
2.2.2. Physical Transactions - JTA
2.2.3. Physical Transactions - CMT
2.2.4. Physical Transactions - Custom
2.2.5. Physical Transactions - Legacy
2.3. Hibernate Transaction Usage
2.4. Transaction Scopes
2.4.1. Session-per-operation
2.4.2. Session-per-request
2.4.3. Conversations
2.4.4. Object identity
2.4.5. Problems and anti-patterns
2.5. Hibernate Transaction API (JTA)
2.5.1. Bean-managed transactions (BMT)
2.5.2. Container-managed transactions (CMT)

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.

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:

org.hibernate.engine.transaction.spi.TransactionFactory is a standard Hibernate service. See Section 7.5.16, “org.hibernate.engine.transaction.spi.TransactionFactory for details.

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.

Important

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.

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:

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:

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.

A 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.

If Hibernate throws an exception, you must rollback your database transaction and close the 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.

The Session caches every object that is in a persistent state.

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.

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.