Hibernate.org Community Documentation

- Prev
- Next

## Chapter 2. Transactions and concurrency control

## 2.1. Defining Transaction

It is important to understand that the term transaction has many different yet related meanings in regards to persistence and Object/Relational Mapping. In most use-cases these definitions align, but that is not always the case.

- Might refer to the physical transaction with the database.

- Might refer to the logical notion of a transaction as related to a persistence context.

- Might refer to the application notion of a Unit-of-Work, as defined by the archetypal pattern.

## Note

This documentation largely treats the physical and logic notions of transaction as one-in-the-same.

## 2.2. Physical Transactions

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.

## Note

`javax.persistence.EntityTransaction` is only available when using resource-local transactions. Hibernate allows access to `org.hibernate.Transaction` regardless of environment.

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

### 2.2.1. Physical Transactions - JDBC

JDBC-based transaction management leverages the JDBC defined methods `java.sql.Connection.commit()` and `java.sql.Connection.rollback()` (JDBC does not define an explicit method of beginning a transaction). In Hibernate, this approach is represented by the `org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory` class.

### 2.2.2. Physical Transactions - JTA

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.

### 2.2.3. Physical Transactions - CMT

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.

### 2.2.4. Physical Transactions - Custom

Its is also possible to plug in a custom 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.

### 2.2.5. Physical Transactions - Legacy

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`

## 2.3. Hibernate Transaction Usage

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 queries that result in loading entities.

## Important

To reduce lock contention in the database, the physical 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`.

## 2.4. Transactional patterns (and anti-patterns)

### 2.4.1. Session-per-operation anti-pattern

This is an 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.

## Note

Using auto-commit does not circumvent database transactions. Instead, when in auto-commit mode, JDBC drivers simply perform each call in an implicit transaction call. It is as if your application called commit after each and every JDBC call.

### 2.4.2. Session-per-request pattern

This is the most common transaction pattern. The term request here relates to the concept of a system that reacts to a series of requests from a client/user. Web applications are a prime example of this type of system, though certainly not the only one. At the beginning of handling such a request, the application opens a Hibernate `Session`, starts a transaction, performs all data related work, ends the transaction and closes the `Session`. The crux of the pattern is the one-to-one relationship between the transaction and the `Session`.

Within this pattern there is a common technique of defining a current session to simplify the need of passing this `Session` around to all the application components that may need access to it. Hibernate provides support for this technique through the `getCurrentSession` method of the `SessionFactory`. The concept of a "current" session has to have a scope that defines the bounds in which the notion of "current" is valid. This is purpose of the `org.hibernate.context.spi.CurrentSessionContext` contract. There are 2 reliable defining scopes:

- First is a JTA transaction because it allows a callback hook to know when it is ending which gives Hibernate a chance to close the `Session` and clean up. This is represented by the `org.hibernate.context.internal.JTASessionContext` implementation of the `org.hibernate.context.spi.CurrentSessionContext` contract. Using this implementation, a `Session` will be opened the first time `getCurrentSession` is called within that transaction.

- Secondly is this application request cycle itself. This is best represented with the `org.hibernate.context.internal.ManagedSessionContext` implementation of the `org.hibernate.context.spi.CurrentSessionContext` contract. Here an external component is responsible for managing the lifecycle and scoping of a "current" session. At the start of such a scope, `ManagedSessionContext`'s `bind` method is called passing in the `Session`. At the end, its `unbind` method is called.

  Some common examples of such "external components" include:

  - `javax.servlet.Filter` implementation

  - AOP interceptor with a pointcut on the service methods

  - A proxy/interception container

## Important

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.

### 2.4.3. Conversations

The session-per-request pattern is not the only valid 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

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

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

Even though we have multiple databases access here, from the point of view of the user, this series of steps represents a single unit of work. 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. These multiple database accesses can only be atomic as a whole 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 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 flush automatically, only explicitly.

Session-per-request-with-detached-objects and session-per-conversation each have advantages and disadvantages.

### 2.4.4. Session-per-application

Discussion coming soon..

## 2.5. Object identity

An application can concurrently access the same persistent state (database row) in two different Sessions. However, an instance of a persistent class is never shared between two `Session` instances. Two different notions of identity exist and come into play here: Database identity and JVM identity.

Example 2.1. Database identity

    foo.getId().equals( bar.getId() )

Example 2.2. JVM identity

    foo==bar

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 anti-patterns. While not recommended, within a `Session` the application could 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.

## 2.6. Common issues

Both the session-per-user-session and session-per-application anti-patterns are susceptible to the following issues. Some of the issues might also arise within the recommended patterns, so ensure that you understand the implications before making a design decision:

- A `Session` is not thread-safe. Things that work concurrently, like HTTP requests, session beans, or Swing workers, will cause race conditions if a `Session` instance is shared. If you keep your Hibernate `Session` in your `javax.servlet.http.HttpSession` (this is discussed later in the chapter), you should consider synchronizing access to your `HttpSession`; otherwise, a user that clicks reload fast enough can use the same `Session` in two concurrently running threads.

- An exception thrown by Hibernate means you have to rollback your database transaction and close the `Session` immediately (this is discussed in more detail later in the chapter). If your `Session` is bound to the application, you have to stop the application. Rolling back the database transaction does not put your business objects back into the state they were at the start of the transaction. This means that the database state and the business objects will be out of sync. Usually this is not a problem, because exceptions are not recoverable and you will have to start over after rollback anyway.

- The `Session` caches every object that is in a persistent state (watched and checked for changes by Hibernate). If you keep it open for a long time or simply load too much data, it will grow endlessly until you get an OutOfMemoryException. One solution is to call `clear()` and `evict()` to manage the `Session` cache, but you should consider an alternate means of dealing with large amounts of data such as a Stored Procedure. Java is simply not the right tool for these kind of operations. Some solutions are shown in Chapter 4, Batch Processing. Keeping a `Session` open for the duration of a user session also means a higher probability of stale data.

- PrevChapter 1. Database access
- Up
- Home
- NextChapter 3. Persistence Contexts