(...)
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:
- A new Session is opened. It doesn’t obtain a database connection at this
point.
- 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!
- 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 (...)