Find this tutorial in:
Try the Tutorial
Transaction annotations make reliable database updates clear, straightforward and scalable. Transaction boundaries protect database consistency by enforcing an all-or-none policy and using the database's locking capabilities for scalability.
Transactions are the common interface to a database's locking capability used to ensure consistent database updates. Because transactional applications use centralized database locking instead of application-specific synchronized blocks they scale to multi-jvm and multi-server deployments without modification.
The tutorial uses transactions to swap teachers for two courses safely. To maintain consistency, either the swap must complete or the database must remain unchanged. Transactions avoid the inconsistent state. The example's database has a single entity, Course. Each course has a course name and an assigned teacher.
The JDBC commit() is the foundation for transactions. At the core, the database provides transactions as part of its locking strategy. The Resin and EJB transactions are mainly programming wrappers around the core database transactions.
When a method is marked as requiring a transaction, the enhanced method essentially creates the above JDBC transaction pattern around the method. Any JDBC calls inside the method are protected by the database's transaction support, including entity bean calls.
Although the JDBC commit forms the foundation of transactions, a more general solution is needed for transaction boundaries: javax.transaction.UserTransaction. Application servers like Resin provide UserTransaction support for JDBC automatically as part of the connection pooling capability.
JDBC commit is inappropriate for transaction boundaries for two reasons: it mixes two separate concepts in one API and is restricted to JDBC. In particular, JDBC commit can't be directly used for persistent object updates like EJB3 or JDO. To use JDBC commit, the method needs the Connection object that needs protection, but a transaction boundary does not have direct access to the Connection. Instead of calling conn.commit() directly, the transaction boundary uses UserTransaction.commit() to call the conn.commit() with some application server help.
Inside the try-finally block, the UserTransaction will automatically call the JDBC transaction methods as necessary and will call the commit() method during the uTrans.commit().
By wrapping the connection transaction support in a UserTransaction, the application server makes transaction control cleaner, and allows for method enhancement of transaction boundaries.
Because the UserTransaction pattern is the same for each transaction boundary, it's important to write it once to avoid bugs and clarify the code. Now, the method is annotated as a transaction boundary and Resin generates the transaction boundary code.
Internally, Resin compiles the annotation into the standard transaction boundary pattern.
The Course is identical to the EJB transaction bean. It uses field enhancement to define the database columns.
The TransactionAttribute marks a method as needing bytecode enhancement to support transactions. The two most common transaction are REQUIRED and SUPPORTS. A REQUIRED method expects to modify the data and wants to ensure the update is consistent. A SUPPORTS method will only read data, so it can avoid the overhead of a transaction.
A transaction is the database equivalent of a synchronized lock. Transactions are somewhat more complicated locks because they need to work with multiple machines and possibly multiple databases, but they're still just sophisticated locks. The typical transaction patterns are similar to familiar lock patterns.
A REQUIRED attribute tells Resin that the method must be protected by a transaction. In this case, the swap needs protection from simultaneous threads trying to swap at the same time.
A SUPPORTS attribute would tell Resin that the method doesn't need a transaction, but the method should join any transaction that already exists.
The first swap in the tutorial switches the teachers using a stateless session bean.
Because the swap method was marked as transaction REQUIRED, Resin will start a transaction if none already exists. If the method exits cleanly, Resin will commit the transaction. If the method throws a RuntimeException, Resin will roll the transaction back.
The swap method will throw a runtime exception if the transaction fails to commit. For example, the databse might detect a deadlock and roll the transaction back.
The transaction annotation lets any Java class use the same transaction capability previously restricted to Session beans. Other than working directly with Java classes, the transaction boundaries are identical to EJB's transaction boundaries.
Stateless session beans have the additional complexities of:
Transaction annotation cuts to the core transaction boundary problem, eliminating the often-unneeded complexity of Session beans. Transaction-enhanced classes and methods are normal Java classes marked with a JDK 1.5 annotation to produce a transaction boundary. Resin provides the same transaction boundaries as Stateless session beans for any Java class.
In contrast, Resin-enhanced classes are normal Java classes. This example enhances a normal servlet to illustrate the differences.
The Resin enhancement currently needs some configuration in the web.xml. The annotation value is configurable. If you want a different transaction attribute, use a different annotation name. Resin provides a com.caucho.transaction.Transaction attribute for applications wishing to avoid confusion with EJB 3.0.