Saturday, July 15, 2017

Spring Transaction Management

When do you want to Transaction Management Programmatically ?
Programmatic transaction management is usually a good idea only if you have a small number of transactional operations. 

On the other hand, if your application has numerous transactional operations, declarative transaction management is usually worthwhile.

Transaction management can be used at class level or method level
You may apply the @Transactional annotation at the method level or the class level. When applying this annotation to a class, all of the public methods within this class will be defined as transactional. Although you can apply @Transactional to interfaces or method declarations in an interface, it's not recommended because it may not work properly with class-based proxies (i.e., CGLIB proxies).

Transaction Propogation

When a transactional method is called by another method, it is necessary to specify how the transaction should be propagated. For example, the method may continue to run within the existing transaction, or it may start a new transaction and run within its own transaction.

Solution : A transaction's propagation behavior can be specified by the propagation transaction attribute. Spring defines seven propagation behaviors.

    public void checkout(List<String> items, String user) {
        for (String item : items) {
            bookShop.purchase(item, user);

Propagation : REQUIRED (default)

However, if the purchase() method is called by a non-transactional method and there's no existing transaction in progress, it will start a new transaction and run within its own transaction.

Propagation : REQUIRES_NEW 

Isolation Level Problem : When multiple transactions of the same application or different applications are operating concurrently on the same dataset, many unexpected problems may arise. You must specify how you expect your transactions to be isolated from one another.

Solution :
The problems caused by concurrent transactions can be categorized into four types:
  • Dirty read: For two transactions T1 and T2, T1 reads a field that has been updated by T2 but not yet committed. Later, if T2 rolls back, the field read by T1 will be temporary and invalid.
  • Nonrepeatable read: For two transactions T1 and T2, T1 reads a field and then T2 updates the field. Later, if T1 reads the same field again, the value will be different.
  • Phantom read: For two transactions T1 and T2, T1 reads some rows from a table and then T2 inserts new rows into the table. Later, if T1 reads the same table again, there will be additional rows.
  • Lost updates: For two transactions T1 and T2, they both select a row for update, and based on the state of that row, make an update to it. Thus, one overwrites the other when the second transaction to commit should have waited until the first one committed before performing its selection.
  • READ_UNCOMMITTED is the lowest isolation level that allows a transaction to read uncommitted changes made by other transactions.
  • similarly there is READ_COMMITTED

Setting the Rollback Transaction Attribute
By default, only unchecked exceptions (i.e., of type RuntimeException and Error) will cause a transaction to roll back, while checked exceptions will not. Sometimes, you may wish to break this rule and set your own exceptions for rolling back.

The exceptions that cause a transaction to roll back or not can be specified by the rollback transaction attribute. Any exceptions not explicitly specified in this attribute will be handled by the default rollback rule (i.e., rolling back for unchecked exceptions and not rolling back for checked exceptions)

            propagation = Propagation.REQUIRES_NEW,
            rollbackFor = IOException.class,
            noRollbackFor = ArithmeticException.class)
    public void purchase(String isbn, String username) throws Exception{
   throw new ArithmeticException();
             //throw new IOException();

Ques : will the transaction be rolled back automatically ?

public void operation() {
    entityManager.persist(new User("Dima"));
    throw new Exception();

Ans is No

Although EJB container default behavior automatically rolls back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on anapplication exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this behavior.

Okaaay… So, if you expect that checked exception will be thrown in your code, you better use rollbackFor in this way:

@Transactional(rollbackFor = Exception.class)
public void operation() {
    entityManager.persist(new User("Dima"));
    throw new Exception();

Ques : Will rollback happen is there is exception in below method

    public Result doStuff(){
        Result res = null;
        try {
          // do stuff
        } catch (Exception e) {

        return res ;

If there is an exception in the method doStuff the transaction isn't rolled back.

declarative approach

@Transactional(rollbackFor={MyException1.class, MyException2.class, ....})
public Result doStuff(){

No comments: