Transaction Method Annotations
Resin 3.0

Features
Installation
Configuration
Web Applications
IOC/AOP
Resources
JSP
Quercus
Servlets and Filters
Databases
Admin (JMX)
CMP
EJB
Amber
EJB 3.0
Security
XML and XSLT
XTP
JMS
Performance
Protocols
Third-party
Troubleshooting/FAQ

Amber
Lifecycle
@Table
Tutorials

CMP Field
CMP Basic
CMP Create
Transactions
CMP Query
CMP Many-to-One
CMP One-to-Many
CMP Many-to-Many
CMP Inherit
Sessions
CMP Create
Tutorials
CMP Query

Find this tutorial in: /usr/local/resin/webapps/resin-doc/amber/tutorial/resin-xa
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.

See also:

  1. Files in this tutorial
  2. Transactions
    1. JDBC commit - underlying database support
    2. UserTransaction - generalizing from JDBC
    3. Method Enhancement - avoiding duplicate code
  3. Entity Bean
    1. Database Schema
    2. Course Entity Implementation
  4. Swap Servlet
    1. @TransactionAttribute
    2. swap
  5. Differences with Stateless Session beans
  6. web.xml

Files in this tutorial

WEB-INF/resin-web.xml resin-web.xml configuration
WEB-INF/classes/META-INF/persistence.xml persistence.xml configuration
WEB-INF/classes/example/Course.java The course bean
WEB-INF/classes/example/SwapServlet.java The swap servlet

Transactions

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.

Potential Results of Swap (multithreaded)
PotionsTransfigurationDescription
McGonnagalSnapeDatabase successfully updated
McGonnagalMcGonnagalInconsistent state prevented by transaction
SnapeSnapeInconsistent state prevented by transaction
SnapeMcGonnagalDatabase unchanged (rollback)

JDBC commit - underlying database support

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.

Basic JDBC transaction pattern
Connection conn = ...;

conn.setAutoCommit(false);
try {
  ... // protected JDBC statements
} finally {
  conn.commit();
}

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.

UserTransaction - generalizing from JDBC

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.

Basic JDBC transaction pattern
UserTransaction uTrans = ...;

uTrans.begin();
try {
  ... // protected JDBC statements
} finally {
  uTrans.commit();
}

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.

Method Enhancement - avoiding duplicate code

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.

Annotated transaction method
@TransactionAttribute
private void swap(Course a, Course b)
{
  String teacher = a.getTeacher();
  a.setTeacher(b.getTeacher());
  b.setTeacher(teacher);
}

Internally, Resin compiles the annotation into the standard transaction boundary pattern.

Enhanced code
private void swap(Course a, Course b)
{
  javax.transaction.UserTransaction uTrans = ...;

  uTrans.begin();
  try {
    String teacher = a.getTeacher();
    a.setTeacher(b.getTeacher());
    b.setTeacher(teacher);
  } finally {
    uTrans.commit();
  }
}

Entity Bean

Database Schema

course.sql
CREATE TABLE amber_xa_courses (
  id INTEGER PRIMARY KEY auto_increment,

  course VARCHAR(250),
  teacher VARCHAR(250)
);

INSERT INTO basic_courses VALUES('Potions', 'Severus Snape');
INSERT INTO basic_courses VALUES('Transfiguration', 'Minerva McGonagall');

Course Entity Implementation

The Course is identical to the EJB transaction bean. It uses field enhancement to define the database columns.

Course.java
@Entity
@Table(name="amber_xa_courses")
public class Course {
  @Id
  @Column(name="id")
  @GeneratedValue
  private int _id;

  @Basic
  @Column(name="course")
  private String _course;

  @Basic
  @Column(name="teacher")
  private String _teacher;
}

Swap Servlet

SwapServlet.java
import javax.persistence.*;
import javax.transaction.UserTransaction;

public class CourseServlet extends HttpServlet {
  @PersistenceContext(name="example")
  private EntityManager _manager;

  ...

  private void doService(PrintWriter out)
    throws Exception
  {
    Course []course = new Course[2];

    course[0] = _manager.find(Course.class, new Integer(1));
    course[1] = _manager.find(Course.class, new Integer(2));

    // swap the courses
    swap(course[0], course[1]);
  }

  @TransactionAttribute(REQUIRED)
  private void swap(Course a, Course b)
  {
    String teacher = a.getTeacher();
    a.setTeacher(b.getTeacher());
    b.setTeacher(teacher);
  }
}

@TransactionAttribute

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.

@TransactionAttribute(REQUIRED)
private void swap(Course a, Course b)
{
  String teacher = a.getTeacher();
  a.setTeacher(b.getTeacher());
  b.setTeacher(teacher);
}

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.

swap

The first swap in the tutorial switches the teachers using a stateless session bean.

// swap the courses using the transactional swap method
swap(course[0], course[1]);

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.

  • REQUIRED automatically rolls back on a RuntimeException
  • REQUIRED joins any existing transaction. In contrast, UserTransaction throws an exception if called in a transaction.

Differences with Stateless Session beans

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:

  • Requires interface and generated stubs.
  • Requires JNDI lookup
  • Pooled bean instances

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.

web.xml

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.

<class-loader>
  <enhancer>
    <method annotation="javax.ejb.TransactionAttribute">
	       type="com.caucho.transaction.enhancer.TransactionEnhancer"/>
  </enhancer>
</class-loader>

Try the Tutorial


CMP Create
Tutorials
CMP Query
Copyright © 1998-2006 Caucho Technology, Inc. All rights reserved.
Resin® is a registered trademark, and HardCoretm and Quercustm are trademarks of Caucho Technology, Inc.