20 - Transactions and Concurrency in Hibernate

20.1 Overview

A transaction is a unit of work in which either all operations must execute or none of them. To understand the importance of transaction, think of a example which applies on all of us. “Transferring Amount from one account to another “ – this operations includes below at least below two steps

  1. Deduct the balance from sender’s account
  2. Add the amount to the receiver’s account.

Now think of the situation where amount is deducted from sender’s account but not gets delivered to receiver account due to some errors. Such issues are managed by transaction management in which both the steps are performed in a single unit of work where either both steps are performed successfully or in case anyone gets failed, it should be roll backed.

There are four important terms which are very important to understand.

  1. Atomic -  As described above , atomicity makes sure that either all operations within a transactions must be successful or none of them.
  2. Consistent- This property makes sure that data should be in consistent state once the transaction is completed.
  3. Isolated-  this property allows multiple users to access the same set of data and each user’s processing should be isolated from others.
  4. Durable – Result of the transaction should be permanent once transaction is completed to avoid any loss of data.

20.2 Hibernate Transaction Management

It is very important to understand the difference between transaction boundaries and transaction demarcation. Starting and end point of a transaction are known as transaction boundaries and technique to identify transaction boundaries are known as transaction demarcation.

Database operations can be performed simultaneously (concurrently) by multiple users and this can end up with serious impacts. In order to control the concurrency , there are two approaches

  1. Optimistic  - Versioning is used in this approach.
  2. Pessimistic – Acquiring Lock mechanism is used in this approach.  

20.3 Programmatic Transaction Demarcation

We can use Core JDBC  transactions by Connection (java,sql.Connection) object . We need to disable the auto commit of connection by using setAutoCommit(false) method on JDBC connection. Doing so, JDBC will not commit the data updates and we have to explicitly call commit() and rollback() methods to commit and rollback the changes.

Hibernated comes with own transaction management (org.hibernate.Transaction) which we have used in all examples we have discussed so far in this tutorial.

If the transaction includes multiple data sources or resources then core JDBC transaction will not suffice and we would need to include Transaction manager which has a capability to commit  the  changes in all data sources in all updates are successful and rolls back if anyone gets failed. This type of transaction management, uses Java Transaction API (JTA) which exposes UserTransaction (javax.transaction.UserTransaction) interface.

Note:  Database connection handling is different between JDBC and JTA transactions. Hibernate gets a connection for every Session and  tries to be as lazy as possible.

Without JTA, Hibernate would hold on to a particular database connection from the beginning until the end of the transaction.

With a JTA configuration, a connection is obtained and used for only a single SQL statement and then is immediately returned back to the connection pool. The application server guarantees that it will provide the same connection during the same transaction, when ever needed for another SQL statement.

20.4 Connection Pooling

We would need connection pooling if application is being used by a multiple users. Multiple connections would be needed. Creating a connection every time is a very expensive operation so we should use connection pooling.

In a web application , application server can manage the connection pool but for standalone we have to rely on some third party solutions.

We can integrate Apache’s connection pooling solutions (DBCP) or can use the C3P0 pooling framework with Hibernate. In fact Hibernate comes with C3P0.

20.4.1 Enabling Connection Pooling in Stand Alone Hibernate Application

Download the connection pool framework jar file and add it in a build path. You can get the required jar files for C3P0 from lib/optional directory of hibernate download.

Once done we need to configure the connection pools properties in hibernate.cfg.xml like below 

       <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider" />

       <property name="hibernate.c3p0.min_size">15</property>

       <property name="hibernate.c3p0.max_size">30</property>

      <property name="hibernate.c3p0.timeout">300</property>

       <property name="hibernate.c3p0.acquire_increment">1</property>

  1. hibernate.connection.provider_class is used to specify the Connection Provider class.
  2. hibernate.c3p0.min_size specifies the minimum connections that will be ready to serve the request every time.
  3. hibernate.c3p0.max_size specifies the maximum number of connections in a pool. This is mandatory property.
  4. hibernate.c3p0.timeout specifies the max idle time for a connection. After this connection will be removed from the pool.
  5. hibernate.c3p0.acquire_increment - is the number of connections created in the pool when all connections in the pool get exhausted.

20.5 Programmatic Transaction in Stand Alone (Non Managed) Hibernate Application

Default transaction factory class for Hibernate is JDBCTransactionFactory and we can configure it using hibernate.transaction.factory_class property. Responsibility of Transaction factory is to instantiate transactions.

Transaction in hibernate standalone application we need to follow below steps

  1. get Session Object from SessionFactory.openSession() . Remember opening a session will not open a connection with database which means opening a session is not expensive.
  2. Get org.hibernate.Transaction object  from session.beginTransaction()  . This step opens the connection with database. Internally this call disables the auto commit of underlying connection.
  3. Call transaction,commit() to persist the changes in database.
  4. Call  transaction.rollback() to rollback the changes. Usually done in case of exceptions (catch block)
Configuration cfg = new Configuration().configure();
factory = cfg.buildSessionFactory();
    Session session =factory.openSession();
    Transaction tx = session.beginTransaction();
    try{
             session.save(entity);
         tx.commit();
       } catch(Exception e)
           {    
              tx.rollback();
           }
              session.close();

20.6 Programmatic Transaction in Managed Environment  using JTA

Managed Environments are managed by the application servers like JBoss, Web sphere etc so this section is more applicable on the web applications. In upcoming chapters we will discuss using Hibernate in Web applications.

You can use open source JTA provider and integrate it with stand alone applications also.

Lets discuss how we can manage transaction and concurrency in managed environments.

a. We need to configure the transaction factory because default JDBC Transaction Factory will not work so we will need to configure it as JTATransactionFactory.

hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory

b. Now we need to inform Hibernate about the JTA implementation we are going to use for our application .As mentioned above it could be any open source JTA or we can use the ones which is provided by application servers in case of web applications. Hibernate supports most of the application server look ups.  To configure we need to use hibernate.transaction.manager_lookup_class property.

 hibernate.transaction.manager_lookup_class=org.hibernate.transaction.WeblogicTransactionManagerLookup

  For Joss we can use

 hibernate.transaction.manager_lookup_class= org.hibernate.transaction.JBossTransactionManagerLookup

As we mentioned in the initial chapters of this tutorial that the creation of SessionFactory object is very expensive and as it is thread safe we need one Session factory per database in entire application. In case of web application, we can create a utility class which will be responsible for creating the session factory and exposing it as static object.

Now,  as the transaction is JTA and it is not longer tied to any session so we cannot get it from  session object. Instead, we can grab the User Transaction object from JNDI as

UserTransaction tx = (UserTransaction)new InitialContext()

.lookup("java:comp/UserTransaction");

Now tx can be used in the same way it is being used in standalone application and we this transaction we can manage multiple sessions  like below pseudo code 

Configuration cfg1 = new Configuration().configure();

factory1 = cfg1.buildSessionFactory();
Session session1 =factory1.openSession();
Configuration cfg2 = new Configuration().configure();

factory2 = cfg2.buildSessionFactory();
Session session2 =factory2.openSession();

UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");

try{
   session1.save(entity);
   session2.save(entity);
    tx.commit();
    } catch(Exception e)
    {    
      tx.rollback();
    }
Session1.close();
Session2.close();

20.7 Controlling Concurrent Access

Isolation one of the ACID property which defines when the changes done by a transaction will be visible to other transactions. Each database vendor provides a transaction isolation level (each transaction assumes that no other transaction is going on) using locking approach (put a lock on the data so no other transaction can access that piece of data and release the lock once done). ORM frameworks like Hibernate improves the transaction isolation level provided by the database with which application is interacting with.

It is important to know that hibernate does not lock anything in memory.

Achieving complete Isolation comes with a price of scalability and performance where as weakening the  isolation level helps in improving performance. With this statement, it is clear that there is no 0 or 1 situation with isolation (either isolation is there or none). Instead there are several level of isolation are supported.

20.7.1 Problem or Issues with  concurrent data access.

Below are the scenarios that can occur with the impact of concurrent data access.

  • Lost Updates – one transaction (T1) updates the data and commits successfully where as the second transaction (T2) fails to commit. In this case updates done by transaction (T1) are lost. This can be the situation when concurrent transactions are not isolated.
  • Dirty Read (Read Uncommitted Data) -  One transaction (T1) reads the uncommitted data (updates are done, but not committed) by another transaction (T2).
  • Phantom Read-  Transaction (T1) executes same query twice and the result sets is different each time, probably because another transaction (T2) has either added or deleted the records.
  • Unrepeatable Read- Transaction (T1) reads the same row twice and state of row is different probably because another transaction (T2) updates the row. Special case would be if T1 updates and committed the same data again, then updates done by T2 will be lost.

20.7.2 Transaction Isolation Levels

 As we mentioned that isolation does not have  0 or 1 state instead there can be different isolation levels. I would like to reiterate that increasing the isolation level will impact the performance. Lets discuss the isolation levels (degree of level on locking the data) and which isolation level can prevent  which issue discussed in section 20.7.1

JTA defines the same isolation level as ANSI –SQL standard does. There are four transaction isolation levels.

  • Read Uncommitted- One transaction can view uncommitted data of another transaction and dirty read, phantom read, unrepeatable reads are allowed. This is the loosest isolation level and is not recommended. 
  • Read Committed – Dirty Reads (Uncommitted Read) are not allowed in this isolation level, but unrepeatable reads and phantom reads are permitted. This approach uses shared read lock and exclusive write lock in which read lock is acquired and released immediately where as write lock is released at the end of the transaction.
  • Repeatable Read-  Dirty Read and Unrepeatable Read is not allowed in this isolation level but phantom reads are allowed. In this isolation level, reading transaction will block all other writing transactions ( but allows other reading transactions ) and any writing transaction will block all other transactions. This will have some scalability issues.
  • Serializable- This is the strictest isolation level and will have  scalability issues. This prevents dirty read, phantom reads , unrepeatable read etc. Transactions are executed serially (one after another) and acquires read and write locks on the entire set of affected data.

Summary

Isolation Level

Phantom Read

Unrepeatable Read

Dirty Read

 

Read Uncommitted

Allowed

Allowed

Allowed

 

Read Committed

Allowed

Allowed

Not Allowed

 

Repeatable Read

Allowed

Not Allowed

Not Allowed

 

Serializable

Not allowed

Not allowed

Not allowed

 

 

20.7.3 How to set a Transaction Isolation level

Every database has default isolation level (either Read Committed or Repeatable Read) and we can override the isolation level using hibernate.connection.isolation property in hibernate.cfg.xml file.

Read uncommitted isolation - 1

Read committed isolation- 2

Repeatable read isolation- 4

Serializable isolation – 8

20.7.4 Enable Versioning in Hibernate

Hibernate has a inbuilt auto versioning of type integer or timestamp at a entity level. To enable versioning , we need to add a property of type int or integer in entity and configuration in mapping of corresponding entity using <version> tag.

Below sample configuration says that there is a property with name version-field in java entity and it is mapped to version column of table.

         <version name="version-field" access="field" column="version"></version>

To use version field as timestamp, define property of data type date and corresponding mapping using <timestamp> tag

        <timestamp name="version-field" access="field" column="timestamp"></timestamp>

Like us on Facebook