Transaction management

You will certainly have worked with transactions. They ensure that a unit of work is atomic, consistent, isolated, and durable. Hibernate either uses JDBC to manage transactions or participates in a global transaction, JTA.

Local transactions

We discussed session scope in Chapter 1, Entity and Session. As we saw, one of the options for setting the session context was a local thread. This means that the scope of the session is within the thread that is executing the code, and Hibernate will start the Transaction using the API provided by JDBC. For further information on transaction management using JDBC, refer to the JavaDoc for java.sql.Connection.

In the old days before Hibernate, we had to use the JDBC API to create drivers, connections, statements, and other objects to read and write from the database. Hibernate does all this work for us and more, and it still allows us to access the low-level JDBC features. For example, the implementation of transaction savepoints is done through the Work interface.

Note

Database transactions allow us define savepoints, so, within the same connection, we can commit a portion of the work and continue with the rest within the same transaction. If a rollback occurs later, it will only roll back the changes up to the savepoint and not the entire transaction. Not all JDBC drivers support savepoints, but most do.

The following code shows one way of implementing savepoints in Hibernate:

session.save(person1);
session.flush();
session.doWork(new Work() {
  @Override
  public void execute(Connection connection)
    throws SQLException {
      connection.commit();
  }
});

Person person2 = randomPerson();
session.save(person2);
transaction.rollback();

There are a few things to note in the code. First, after we call the save method on the session, we call flush to ensure that the queued up actions are executed in the database. Next, we access the JDBC connection directly through the Work API to commit the transaction up to now. And, as you may notice, at the end of the work, we roll back the transaction; but, at this point, person1 is stored in the database and only person2 is rolled back.

You shouldn't have to use savepoints in most cases. This is being shared only for rare cases.

The Java Transaction API

JTA, was created to manage multiple transactional resources in one unit of work, for example, multiple databases and message queues. (JMS)

It is very common to have to read and write to multiple databases and message queues in an enterprise arena, for example, when implementing Enterprise Information Systems (EIS) to write to multiple databases.

JTA uses the XA protocol to allow resources to participate in a global transaction so that work can be committed or rolled back simultaneously in all systems. For this reason, the transactional resource must support the XA Protocol, and very likely you will have to use a different driver class.

Note

Consult the JDBC driver documentation for a specific implementation such as DB2, Postgres, Oracle, and so on. For example, the XA driver for Oracle is a class called oracle.jdbc.xa.client.OracleXADataSource.

JTA requires a transaction manager (JTS) that can demarcate transactions and manage all participating resources. All Java EE servers can act as JTA managers because JTA is part of the JEE specifications.

Some might think that Tomcat is a complete JEE server, but it's not. It only implements a subset of JEE specifications, such as Servlet, JSP, and JNDI. There exist JTA managers that you can run with Tomcat, for example, JOTM and Atomikos. The same people likely believe that the Spring framework implements JTA, and that is not true. Spring offers a very clean interface for transaction management but it uses the JEE application server for global transactions.

As discussed earlier, Hibernate can add a listener to global transaction events if a transaction manager exists. If you set your session context to jta, in the Hibernate configuration file, then Hibernate will add a javax.transaction.Synchronization class for receiving notifications, so it can do a session clean up, for example, flushing or discarding session data.

We discussed savepoints earlier and how to commit a portion of a transaction when working with local resources. JTA doesn't support savepoints for two reasons. Firstly, you can't commit or rollback transactions, only the JTS, that is, the transaction manager, is allowed to issue a commit because it has to perform two-phase commit. Secondly, nested transactions are not allowed in JTA. But there are ways to implement savepoints when you are in the JTA environment.

One way of doing this is to define your transaction boundaries at the JMS endpoint and let a message-driven Bean (MDB) take over the portion of the data that you want to commit regardless of what happens next in the execution path. But, you have to be aware of the fact that JMS allows you to propagate transactions all the way to the receiving MDB. You would have to configure your JMS session to reduce the scope of the transaction to the message queue and set it to auto-commit, meaning that, once the message reaches the queue successfully, the transaction doesn't propagate any further. (See the Javadoc for JMS sessions.) That way, your MDB can start a new transaction and commit the work without worrying about the rest of the execution.

Another way is to use EJBs directly and use a TX_REQUIRES_NEW handling mechanism in a CMT configuration. That way, even if you start a JTA transaction in your web application, when it reaches the EJB, the first transaction is suspended and a new transaction is started. You can commit that work regardless of what happens in the rest of the execution path.

JTA is great for making sure that enterprise resources stay synchronized. But a rogue resource can impact your application and all other resources. When working with multiple resources, pay attention to the performance of work units.

Compensating transactions

You can't always use JTA to coordinate multiple resources, for example, if one of these resources is a web service that doesn't support transaction, such as RESTFul services. In that case, you would have to implement, as part of your solution, a rollback mechanism on the non-transactional resource, or at least notify the resource that a rollback has occurred.

In fact, Hibernate envers would make a perfect solution for rolling back to a previous version of data. We discussed envers in Chapter 6, Events, Interceptors and Envers.

Compensating transactions are simply the Undo functionality, and in this case, the word "transaction" refers to a business transaction and not a database transaction because you are literally changing business data and restoring it to the values before the unit of work had started. A good example is canceling a shopping order because the payment was rejected further in the process.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.122.244