A transaction can be defined as an indivisible unit of work comprised of several operations, all or none of which must be performed in order to preserve data integrity. For example, a transfer of 00 from your checking account to your savings account would consist of two steps: debiting your checking account by 00 and crediting your savings account with 00. To protect data integrity and consistency -- and the interests of the bank and the customer -- these two operations must be applied together or not at all. Thus, they constitute a transaction.
Properties of a transaction
All transactions share these properties: atomicity, consistency, isolation, and durability (represented by the acronym ACID).
- Atomicity: This implies indivisibility; any indivisible operation (one which will either complete fully or not at all) is said to be atomic.
- Consistency: A transaction must transition persistent data from one consistent state to another. If a failure occurs during processing, the data must be restored to the state it was in prior to the transaction.
- Isolation: Transactions should not affect each other. A transaction in progress, not yet committed or rolled back (these terms are explained at the end of this section), must be isolated from other transactions. Although several transactions may run concurrently, it should appear to each that all the others completed before or after it; all such concurrent transactions must effectively end in sequential order.
- Durability: Once a transaction has successfully committed, state changes committed by that transaction must be durable and persistent, despite any failures that occur afterwards.
A transaction can thus end in two ways: a commit, the successful execution of each step in the transaction, or a rollback, which guarantees that none of the steps are executed due to an error in one of those steps.
Transaction isolation levels
measures concurrent transactions' capacity to view data that have been updated, but not yet committed, by another transaction. If other transactions were allowed to read data that are as-yet uncommitted, those transactions could end up with inconsistent data were the transaction to roll back, or end up waiting unnecessarily were the transaction to commit successfully.
A higher isolation level means less concurrence and a greater likelihood of performance bottlenecks, but also a decreased chance of reading inconsistent data. A good rule of thumb is to use the highest isolation level that yields an acceptable performance level. The following are common isolation levels, arranged from lowest to highest:
- ReadUncommitted: Data that have been updated but not yet committed by a transaction may be read by other transactions.
- ReadCommitted: Only data that have been committed by a transaction can be read by other transactions.
- RepeatableRead: Only data that have been committed by a transaction can be read by other transactions, and multiple reads will yield the same result as long as the data have not been committed.
- Serializable: This, the highest possible isolation level, ensures a transaction's exclusive read-write access to data. It includes the conditions of
RepeatableReadand stipulates that all transactions run serially to achieve maximum data integrity. This yields the slowest performance and least concurrency. The term serializable in this context is absolutely unrelated to Java's object-serialization mechanism and the
Transaction support under J2EE
The Java 2 Enterprise Edition (J2EE) platform consists of the specification, compatibility test suite, application-development blueprints, and reference implementation. Numerous vendors provide application servers/implementations based on the same specification. J2EE components are meant to be specification-centric rather than product-centric (they are built to a specification, rather than around a particular application-server product). J2EE applications include components that avail of the infrastructural services provided by the J2EE container and server, and therefore need to focus only on "business logic." J2EE supports flexible deployment and customization in the target production environment, using declarative attributes provided by a deployment descriptor. J2EE aims to protect IT efforts and reduce application-development costs. J2EE components may be built in-house or procured from outside agencies, which can result in flexibility and cost benefits for your IT department.
Transaction support is an important infrastructural service offered by the J2EE platform. The specification describes the Java Transaction API (JTA), whose major interfaces include
UserTransaction is exposed to application components, while the underlying interaction between the J2EE server and the JTA
TransactionManager is transparent to the application components. The
TransactionManager implementation supports the server's control of (container-demarcated) transaction boundaries. The JTA
UserTransaction and JDBC's transactional support are both available to J2EE application components.
The J2EE platform supports two transaction-management paradigms: declarative transaction demarcation and programmatic transaction demarcation.
Declarative transaction demarcation
Declarative transaction management refers to a non-programmatic demarcation of transaction boundaries, achieved by specifying within the deployment descriptor the transaction attributes for the various methods of the container-managed EJB component. This is a flexible and preferable approach that facilitates changes in the application's transactional characteristics without modifying any code. Entity EJB components must use this container-managed transaction demarcation.
What is a transaction attribute?
A transaction attribute supports declarative transaction demarcation and conveys to the container the intended transactional behavior of the associated EJB component's method. Six transactional attributes are possible for container-managed transaction demarcation:
- Required: A method with this transactional attribute must be executed within a JTA transaction; depending on the circumstances, a new transaction context may or may not be created. If the calling component is already associated with a JTA transaction, the container will invoke the method in the context of said transaction. If no transaction is associated with the calling component, the container will automatically create a new transaction context and attempt to commit the transaction when the method completes.
- RequiresNew: A method with this transactional attribute must be executed in the context of a new transaction. If the calling component is already associated with a transaction context, that transaction is suspended, a new transaction context is created, and the method is executed in the context of the new transaction, after whose completion the calling component's transaction is resumed.
- NotSupported: A method with this transactional attribute is not intended to be part of a transaction. If the calling component is already associated with a transaction context, the container suspends that transaction, invokes the method unassociated with a transaction, and upon completion of the method, resumes the calling component's transaction.
- Supports: A method with this transactional attribute supports the calling component's transactional situation. If the calling component does not have any transactional context, the container will execute the method as if its transaction attribute was
NotSupported. If the calling component is already associated with a transactional context, the container will execute the method as if its transactional attribute was
- Mandatory: A method with this transactional attribute must only be called from the calling component's transaction context. Otherwise, the container will throw a
- Never: A method with this transactional attribute should never be called from a calling component's transaction context. Otherwise, the container will throw a
Methods within the same EJB component may have different transactional attributes for optimization reasons, since all methods may not need to be transactional. The isolation level of entity EJB components with container-managed persistence is constant, as the DBMS default cannot be changed. The default isolation level for most relational database systems is usually
Programmatic transaction demarcation
Programmatic transaction demarcation is the hard coding of transaction management within the application code. Programmatic transaction demarcation is a viable option for session EJBs, servlets, and JSP components. A programmatic transaction may be either a JDBC or JTA transaction. For container-managed session EJBs, it is possible -- though not in the least recommended -- to mix JDBC and JTA transactions.
JDBC transactions are controlled by the DBMS's transaction manager. The JDBC Connection -- the implementation of the
interface - supports transaction demarcation. JDBC connections have their auto-commit flag turned on by default, resulting in the commitment of individual SQL statements immediately upon execution. However, the auto-commit flag can be programmatically changed by calling the
method false with the argument. Afterward, SQL statements may be serialized to form a transaction, followed by a programmatic
. Thus, JDBC transactions are delimited with the commit or rollback. A particular DBMS's transaction manager may not work with heterogeneous databases. JDBC drivers that support distributed transactions provide implementations for
and two new interfaces of JDBC 2.0,
JTA transactions are controlled and coordinated by the J2EE transaction manager. JTA transactions are available to all the J2EE components -- servlets, JSPs, and EJBs -- for programmatic transaction demarcation. Unlike JDBC transactions, in JTA transactions the transaction context propagates across the various components without additional programming effort. In J2EE server products, which support the distributed two-phase commit protocol, a JTA transaction can span updates to multiple diverse databases with minimal coding effort. However, JTA supports only flat transactions, which have no nested (child) transactions.
javax.transaction.UserTransaction interface defines methods that allow applications to define transaction boundaries and explicitly manage transactions. The
UserTransaction implementation also provides the application components -- servlets, JSPs, EJBs (with bean-managed transactions) -- with the ability to control transaction boundaries programmatically. EJB components can access
EJBContext using the
getUserTransaction() method. The methods specified in the
UserTransaction interface include
setTransactionTimeout(int seconds). The J2EE server provides the object that implements the
javax.transaction.UserTransaction interface and makes it available via JNDI lookup. The isolation level of session EJB components and entity EJB components that use bean-managed persistence may be programmatically changed using the
setTransactionIsolation() method; however, changing the isolation level in mid-transaction is not recommended.
Optional aspects of J2EE transactional support
Some aspects of the J2EE platform are optional, which may be due to evolving standards and introducing new concepts gradually (in terms of Internet time). For example, in the EJB 1.0 specification, entity beans (and container-managed persistence) was a relatively new concept and an optional feature. Support for entity beans became mandatory about a year later in the EJB 1.1 specification because of high market acceptance and demand. As products mature and support more sophisticated features, non-trivial features may be made a mandatory part of the specification. The following are some optional transaction-related aspects:
- Multiple database connections within a transaction context and the two-phase commit protocol: The J2EE 1.2 specification does not require a J2EE server implementation to support access to multiple JDBC databases within a transaction context (and support the two-phase commit protocol). The
javax.transaction.xa.XAResourceinterface is a Java mapping of the industry-standard
XAinterface based on X/Open CAE specification. (See Resources.) X/Open is a consortium of vendors who aim to define a Common Applications Environment that supports application portability. Support for the multiple JDBC data sources,
javax.transaction.xa.XAResource, two-phase commit, etc., is optional in the current specification, though the next version will likely mandate such support. Sun Microsystems's J2EE reference implementation, for instance, supports access to multiple JDBC databases within the same transaction using the two-phase commit protocol.
- Transactional support for application clients and applets: The J2EE 1.2 specification does not require that transactional support be made available to application clients and applets. Some J2EE servers may provide such support in their J2EE server products. As a design practice, transaction management within application clients should be avoided as much as possible, in keeping with the thin client and three-tier model. Also, a transaction, being a precious resource, must be distributed sparingly.
- Inter-Web-component transaction context propagation: The J2EE 1.2 specification does not mandate that the transaction context be propagated between Web components. Typically, Web components like servlets and JSPs need to make calls on (session) EJB components, rather than to other Web components.
Transactional support and portability
In the interest of component portability, it is important for you -- the designer and developer -- to understand which aspects of transactional support are mandatory and which are optional. In the J2EE model, components are written against a specification and are meant to be deployed on J2EE-compliant application servers from various vendors -- all in the interest of protecting IT investment and cross-J2EE-server portability. But if a crucial transactional functionality needs an optional transactional feature, take adequate care to declare, document, and highlight the dependency clearly, explicitly, and as early as possible.
J2EE's declarative transaction demarcation approach is more elegant than programmatic transaction demarcation. At the same time, using declarative transaction demarcation means relinquishing control of the isolation level, since one is limited to the default level provided in the DBMS. If you must use programmatic transaction demarcation, JTA transactions are generally preferred over JDBC transactions. JTS transactions, however, cannot be nested. In the interest of portability, be aware of the optional and mandatory aspects of transactional support in the J2EE platform. Against this background, your application's specific transactional needs will naturally govern your choice of transaction management strategy.
Learn more about this topic
- Designing Enterprise Applications with the Java 2 Platform, Enterprise Edition, Nicholas Kassem (Addison-Wesley, 2000)
- Java 2 Platform, Enterprise Edition specification, v1.2
- Enterprise JavaBeans specification, v1.1
- X/Open CAE specification -- Distributed Transaction Processing
- CORBAservices -- Common Object Services Specification, Object Management Group, Inc.