In the previous article of this series related to Spring Framework,we discussed how easy it is to Unit Test the POJOs in Spring and as well little introduction on how effective is Spring in implementing Transaction Management .In continuation of that article,today we will explore more about configuring Transaction Management in Spring for different datasources
Transaction Management in Spring
Sometimes the fundamental error in judgement a Java Developer makes when dealing with transactions are using global transactions instead of local transactions.First let us understand these two types of transactions.
What is a transaction ?
A database transaction is a unit of interaction with a database management system or similar system that is treated in a coherent and reliable way independent of other transactions. In general, a database transaction must be atomic, meaning that it must be either entirely completed or aborted. Ideally, a database system will guarantee the properties of Atomicity, Consistency, Isolation and Durability (ACID) for each transaction.
Purpose of Transaction
In database products the ability to handle transactions allows the user to ensure that integrity of a database is maintained.
A single transaction might require several queries, each reading and/or writing information in the database. When this happens it is usually important to be sure that the database is not left with only some of the queries carried out. For example, when doing a money transfer, if the money was debited from one account, it is important that it also be credited to the depositing account. Also, transactions should not interfere with each other. For more information about desirable transaction properties, see ACID.
In order to reflect the correct state of reality in the system, a transaction should have the following properties.
- Atomicity: This is the all-or-nothing property. Either the entire sequence of operations is successful or unsuccessful. A transaction should be treated as a single unit of operation. Completed transactions are only committed and incomplete transactions are rolled back or restored to the state where it started. There is absolutely no possibility of partial work being committed.
- Consistency: A transaction maps one consistent state of the resources (e.g. database) to another. Consistency is concerned with correctly reflecting the reality of the state of the resources. Some of the concrete examples of consistency are referential integrity of the database, unique primary keys in tables etc.
- Isolation: A transaction should not reveal its results to other concurrent transactions before it commits. Isolation assures that transactions do not access data that is being concurrently updated. The other name for isolation is serialization.
- Durability: Results of completed transactions have to be made permanent and cannot be erased from the database due to system failure. Resource managers ensure that the results of a transaction are not altered due to system failures.
A simple transaction is usually issued to the database system in a language like SQL in this form:
- Begin the transaction
- Execute several queries (although any updates to the database aren't actually visible to the outside world yet)
- Commit the transaction (updates become visible if the transaction is successful)
If one of the queries fails the database system may rollback either the entire transaction or just the failed query. This behaviour is dependent on the DBMS in use and how it is set up. The transaction can also be rolled back manually at any time before the commit.
Local vs Global TransactionsLocal transactions are specific to a single transactional resource (a JDBC connection, for example), whereas global transactions are managed by the container and can span multiple transactional resources.
Unlike a centralized computing environment where application components and resources are located at a single site, and transaction management only involves a local data manager running on a single machine, in a distributed computing environment all the resources are distributed across multiple systems. In such a case transaction management needs to be done both at local and global levels. A local transaction is one which involves activities in a single local resource manager. A distributed or a global transaction is executed across multiple systems, and its execution requires coordination between the global transaction management system and all the local data managers of all the involved systems. The Resource Manager and Transaction Manager (TM), also known as a transaction processing monitor (TP monitor), are the two primary elements of any transactional system. In centralized systems, both the TP monitor and the resource manager are integrated into the DBMS server. To support advanced functionalities required in a distributed component-based system, separation of TP monitor from the resource managers is required.
Local transactions are easy to manage, and because most operations work with just one transactional resource (such as a JDBC transaction), using local transactions is enough. However, if you are not using Spring, you still have a lot of transaction management code to write, and if in the future the scope of the transaction needs to be extended across multiple transactional resources, you have to drop the local transaction management code and rewrite it to use global transactions.
A global or distributed transaction consists of several subtransactions and is treated as a single recoverable atomic unit. The global transaction manager is responsible for managing distributed transactions by coordinating with different resource managers to access data at several different systems. Since multiple application components and resources participate in a transaction, it's necessary for the transaction manager to establish and maintain the state of the transaction as it occurs.Global transactions in non-Spring application are, in most cases, coded using JTA, which is a complex API that depends on JNDI. This means that you have to use a J2EE application server
Two-Phase Commit (2PC) Protocol
The two-phase commit protocol enables the Atomicity in a distributed transaction scenario. The system module responsible for this protocol is usually called a transaction manager or a coordinator. As the name implies, there are two phases to the protocol. In the first phase, the coordinator asks each participant to vote on a commit or a rollback. This is accomplished by sending a so-called prepare request to each participant. When a participant votes for a commit, it loses its right to roll back independently, meaning that it has to wait for the final outcome received from the coordinator. The first phase ends when the coordinator receives all votes or if a timeout occurs. The second phase starts with the final decision made by the coordinator. In the case of a timeout or at least one "rollback" vote, the decision to roll back is sent to each participant that voted for "commit" in the first phase. As a result, all data modifications at all places involved are rolled back. Should all participants vote to commit, then and only then, the coordinator decides to perform a global commit and sends a commit notification to all participants. Consequently, all the work at all places is committed.
The complexity of the two-phase commit relates not only to the distributed nature of a transaction, but also to a possible non-atomic outcome of a transaction, i.e. heuristics. For example, the first participant may commit changes during phase two, while the second participant encounters a hardware failure when saving changes to the disk. Being able to roll back or at least notify the errors to recover the system into the original state is an important part of the process.
By persisting intermediate steps of the 2PC, that is, logging abort, ready to commit, and commit messages, the protocol provides a certain degree of reliability in case the coordinator or participants fail in the midst of transaction processing. The two-phase commit protocol can be implemented in a synchronous or asynchronous manner with variations to its actual execution.
Programmatic vs Declarative TransactionsThe Java EE container implements support for transactions and facilitates the ACID properties required by the application logic. The container provides an implementation for the two-phase commit protocol between a transaction manager and underlying resources such as the database or messaging provider. The Java EE container is also responsible for the transaction context propagation and provides support for a distributed two-phase commit. With a distributed two-phase commit, a Java EE application can modify data across multiple application servers as if it is a single transaction.
The decision for whether to use programmatic or declarative transaction support depends on the level of transaction control and complexity required by the application design. With the declarative transaction support boundaries and individual properties of a transaction are specified in a deployment descriptor . With programmatic support to a transaction application logic encapsulates transactional characteristics in the code. A POJO object has to use the programmatic transaction demarcation. And because of Spring's Inversion of Control and dependency injection CMT is now also available for POJO applications.
Container-Managed Transactions(EJB 2.1) vs. Spring’s Declarative Transaction management(Spring 1.2):
EJB’s Transaction attribute:
Required
RequiresNew
Mandatory
NotSupported
Supports
Never
Spring’s Progagation behavior:
Interface org.springframework.transaction.TransactionDefinition
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
(b)
EJB’s Isolation level:
Interface java.sql.Connection
TRANSACTION_NONE
TRANSACTION_READ_COMMITTED
TRANSACTION_READ_UNCOMMITTED
TRANSACTION_REPEATABLE_READ
TRANSACTION_SERIALIZABLE
Spring’s Isolation level:
Interface org.springframework.transaction.TransactionDefinition
ISOLATION_DEFAULT
ISOLATION_READ_COMMITTED
ISOLATION_READ_UNCOMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
(c)
Rolling Back a Container-Managed Transaction
There are two ways to roll back a container-managed transaction.
First, if a system exception is thrown, the container will automatically roll back the transaction. Second, by invoking the setRollbackOnly method of the EJBContext interface, the bean method instructs the container to roll back the transaction. If the bean throws an application exception, the rollback is not automatic but can be initiated by a call to setRollbackOnly.
Spring’s Roll back rules
Transaction can be declared to roll back or not based on exceptions that are thrown during the course of the transaction.
By default, transactions are rolled back only on runtime exceptions and not on checked exceptions.
Bean-Managed Transactions(EJB 2.1) vs. Spring’s Programmatic Transaction management(Spring 1.2):
In a bean-managed transaction, the code in the session or message-driven bean explicitly marks the boundaries of the transaction. An entity bean cannot have bean-managed transactions; it must use container-managed transactions instead.
Although beans with container-managed transactions require less coding, they have one limitation: When a method is executing, it can be associated with either a single transaction or no transaction at all. If this limitation will make coding your bean difficult, you should consider using bean-managed transactions.
Spring provides two means of programmatic transaction management:
Using the TransactionTemplate
Using a PlatformTransactionManager implementation directly
ejb-jar.xml
<ejb-jar>
<enterprise-beans>
<!– A minimal session EJB deployment –>
<session>
<ejb-name>PostingEJB</ejb-name>
<home>ejbs.PostingHome</home>
<remote>ejbs.Posting</remote>
<ejb-class>ejbs.PostingBean</ejb-class>
<!– or Stateless –>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
<!– OPTIONAL, can be many. How the container is to manage
transactions when calling anEJB’s business methods –>
<container-transaction>
<!– Can specify many methods at once here –>
<method>
<ejb-name>EmployeeRecord</ejb-name>
<method-name>*</method-name>
</method>
<!– NotSupported|Supports|Required|RequiresNew|Mandatory|Never –>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Payroll</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Spring applicationcontext.xml
<beans>
<bean id=”petStore”
class=”org.springframework.transaction.interceptor.TransactionProxyFactoryBean”>
<property name=”transactionManager” ref=”txManager”/>
<property name=”target” ref=”petStoreTarget”/>
<property name=”transactionAttributes”>
<props>
<prop key=”insert*”>
PROPAGATION_REQUIRED,-MyCheckedException</prop>
<prop key=”update*”>
PROPAGATION_REQUIRED</prop>
<prop key=”*”>
PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
The following are the key differences from EJB CMT (from Introduce the Spring Framework)
a. Transaction management can be applied to any POJO. We recommend that business objects implement interfaces, but this is a matter of good programming practice, and is not enforced by the framework.
b. Programmatic rollback can be achieved within a transactional POJO through using the Spring transaction API. We provide static methods for this, using ThreadLocal variables, so you don’t need to propagate a context object such as an EJBContext to ensure rollback.
c. You can define rollback rules declaratively. Whereas EJB will not automatically roll back a transaction on an uncaught application exception (only on unchecked exceptions, other types of Throwable and “system” exceptions), application developers often want a transaction to roll back on any exception. Spring transaction management allows you to specify declaratively which exceptions and subclasses should cause automatic rollback. Default behaviour is as with EJB, but you can specify automatic rollback on checked, as well as unchecked exceptions. This has the important benefit of minimizing the need for programmatic rollback, which creates a dependence on the Spring transaction API (as EJB programmatic rollback does on the EJBContext).
d. Because the underlying Spring transaction abstraction supports savepoints if they are supported by the underlying transaction infrastructure, Spring’s declarative transaction management can support nested transactions, in addition to the propagation modes specified by EJB CMT (which Spring supports with identical semantics to EJB). Thus, for example, if you have doing JDBC operations on Oracle, you can use declarative nested transactions using Spring.
You cannot control the atomicity, consistency, and durability of a transaction, but you can control the transaction propagation and timeout, which set whether the transaction should be read-only and specify the isolation level.
Spring encapsulates all these settings in a TransactionDefinition interface. This interface is used in the core interface of the transaction support in Spring, the PlatfromTransactionManager, whose implementations perform transaction management on a specific platform, such as JDBC or JTA. The core method, PlatformTransactionManager.getTransaction(), returns a TransactionStatus interface, which is used to control the transaction execution, more specifically to set the transaction result and to check whether the transaction is read-only or whether it is a new transaction.
Exploring the TransactionDefinition Interface
As we mentioned earlier, the TransactionDefinition interface controls the properties of a transaction.
import java.sql.Connection;
public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
}
Using the TransactionStatus Interface
This interface allows a transactional manager to control the transaction execution. The code can check whether the transaction is a new one, or whether it is a read- only transaction and it can initiate a rollback.
public interface TransactionStatus {
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
0 comments:
Post a Comment