Non-transactional data access and the auto-commit mode

(This is an excerpt of chapter 10 of the book Java Persistence with Hibernate. If you read this, you are probably one of the forum posters who asked the usual FAQs about the auto-commit mode.)

 

Many DBMSs enable the so called autocommit mode on every new database connection by default. The autocommit mode is useful for ad hoc execution of SQL.

 

Imagine that you connect to your database with an SQL console and that you run a few queries, and maybe even update and delete rows. This interactive data access is ad hoc; most of the time you don’t have a plan or a sequence of statements that you consider a unit of work. The default autocommit mode on the database connection is perfect for this kind of data access — after all, you don’t want to type begin a transaction and end a transaction for every SQL statement you write and execute. In autocommit mode, a (short) database transaction begins and ends for each SQL statement you send to the database. You’re working effectively nontransactionally, because there are no atomicity or isolation guarantees for your session with the SQL console. (The only guarantee is that a single SQL statement is atomic.)

 

An application, by definition, always executes a planned sequence of statements. It seems reasonable that you therefore always create transaction boundaries to group your statements into units that are atomic. Therefore, the autocommit mode has no place in an application.

 

Debunking auto-commit myths

Many developers still like to work with an autocommit mode, often for reasons that are vague and not well defined. Let’s first debunk a few of these reasons before we show you how to access data nontransactionally if you want (or have) to:

  • Many application developers think they can talk to a database outside of a transaction. This obviously isn’t possible; no SQL statement can be send to a database outside of a database transaction. The term nontransactional data access means there are no explicit transaction boundaries, no system transaction, and that the behavior of data access is that of the autocommit mode. It doesn’t mean no physical database transactions are involved.
  • If your goal is to improve performance of your application by using the autocommit mode, you should think again about the implications of many small transactions. Significant overhead is involved in starting and ending a database transaction for every SQL statement, and it may decrease the performance of your application.
  • If your goal is to improve the scalability of your application with the auto-commit mode, think again: A longer-running database transaction, instead of many small transactions for every SQL statement, may hold database locks for a longer time and probably won’t scale as well. However, thanks to the Hibernate persistence context and write-behind of DML, all write locks in the database are already held for a short time. Depending on the isolation level you enable, the cost of read locks is likely negligible. Or, you may use a DBMS with multiversion concurrency that doesn’t require read locks (Oracle, PostgreSQL, Informix, Firebird), because readers are never blocked by default.
  • Because you’re working nontransactionally, not only do you give up any transactional atomicity of a group of SQL statements, but you also have weaker isolation guarantees if data is modified concurrently. Repeatable reads based on read locks are impossible with autocommit mode. (The persistence context cache helps here, naturally.)

 

Many more issues must be considered when you introduce nontransactional data access in your application. We’ve already noted that introducing a new type of transaction, namely read-only transactions, can significantly complicate any future modification of your application. The same is true if you introduce nontransactional operations.

 

You would then have three different kinds of data access in your application: in regular transactions, in read-only transactions, and now also nontransactional, with no guarantees. Imagine that you have to introduce an operation that writes data into a unit of work that was supposed to only read data. Imagine that you have to reorganize operations that were nontransactional to be transactional.

Our recommendation is to not use the autocommit mode in an application, and to apply read-only transactions only when there is an obvious performance benefit or when future code changes are highly unlikely. Always prefer regular ACID transactions to group your data-access operations, regardless of whether you read or write data.

 

Having said that, Hibernate and Java Persistence allow nontransactional data access. In fact, the EJB 3.0 specification forces you to access data nontransactionally if you want to implement atomic long-running conversations. We’ll approach this subject in the next chapter. Now we want to dig a little deeper into the consequences of the autocommit mode in a plain Hibernate application. (Note that, despite our negative remarks, there are some good use cases for the autocommit mode. In our experience autocommit is often enabled for the wrong reasons and we wanted to wipe the slate clean first.)

 

Working nontransactionally with Hibernate

Look at the following code, which accesses the database without transaction boundaries:

Session session = sessionFactory.openSession(); 
session.get(Item.class, 123l); 
session.close(); 

 

By default, in a Java SE environment with a JDBC configuration, this is what happens if you execute this snippet:

1. A new Session is opened. It doesn’t obtain a database connection at this point.

2. The call to get() triggers an SQL SELECT. The Session now obtains a JDBC Connection from the connection pool. Hibernate, by default, immediately turns off the autocommit mode on this connection with setAutoCommit(false). This effectively starts a JDBC transaction!

3. The SELECT is executed inside this JDBC transaction. The Session is closed, and the connection is returned to the pool and released by Hibernate — Hibernate calls close() on the JDBC Connection. What happens to the uncommitted transaction?

 

The answer to that question is, “It depends!” The JDBC specification doesn’t say anything about pending transactions when close() is called on a connection. What happens depends on how the vendors implement the specification. With Oracle JDBC drivers, for example, the call to close() commits the transaction! Most other JDBC vendors take the sane route and roll back any pending transaction when the JDBC Connection object is closed and the resource is returned to the pool.

Obviously, this won’t be a problem for the SELECT you’ve executed, but look at this variation:

Session session = getSessionFactory().openSession(); 
Long generatedId = session.save(item); 
session.close(); 

 

This code results in an INSERT statement, executed inside a transaction that is never committed or rolled back. On Oracle, this piece of code inserts data permanently; in other databases, it may not. (This situation is slightly more complicated: The INSERT is executed only if the identifier generator requires it. For example, an identifier value can be obtained from a sequence without an INSERT. The persistent entity is then queued until flush-time insertion — which never happens in this code. An identity strategy requires an immediate INSERT for the value to be generated.)

 

 

We haven’t even touched on autocommit mode yet but have only highlighted a problem that can appear if you try to work without setting explicit transaction boundaries. Let’s assume that you still think working without transaction demarcation is a good idea and that you want the regular autocommit behavior. First, you have to tell Hibernate to allow autocommitted JDBC connections in the

Hibernate configuration:

<property name="connection.autocommit">true</property> 

 

With this setting, Hibernate no longer turns off autocommit when a JDBC connection is obtained from the connection pool — it enables autocommit if the connection isn’t already in that mode. The previous code examples now work predictably, and the JDBC driver wraps a short transaction around every SQL statement that is send to the database — with the implications we listed earlier.

 

In which scenarios would you enable the autocommit mode in Hibernate, so that you can use a Session without beginning and ending a transaction manually? Systems that benefit from autocommit mode are systems that require on-demand (lazy) loading of data, in a particular Session and persistence context, but in which it is difficult to wrap transaction boundaries around all code that might trigger on-demand data retrieval. This is usually not the case in web applications that follow the design patterns we discuss in chapter 16. On the other hand, desktop applications that access the database tier through Hibernate often require on-demand loading without explicit transaction boundaries. For example, if you double-click on a node in a Java Swing tree view, all children of that node have to be loaded from the database. You'd have to wrap a transaction around this event manually; the autocommit mode is a more convenient solution. (Note that we are not proposing to open and close Sessions on demand!)

... get Java Persistence with Hibernate to read more.