Transactions delivered by the server distribution are dependant on the JLupinApplicationContainer interface implementation (Please read more about this topic in the JLupinApplicationContainer chapter). Because the default JLupinApplicationContainer implementation is based on Spring framework, we need to carefully read the documentation concerning this topic in order to properly use the transaction model:

  1. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

The following article will also be helpful (transaction model uses):

  1. http://examples.javacodegeeks.com/enterprise-java/spring/jdbc/spring-transaction-management-example-with-jdbc-example/

TRANSACTION SCOPES:

1)The REQUEST_SCOPE transactions type

Diagram 1. Request Scope Transaction Overview

Transactions with the request scope - namely, the transaction are started at the moment of the request's entry to the application and the object named "jLupinTransactionManager" is searched by the JLupinServiceProcessor object. The 'begin()' method On that object will be executed before the execution of the business logic. After successful completion of processing the request, the 'commit' method will be executed. If, however, an exception will be thrown which will be processed by the JLupinExceptionHandler interface implementation, then the "rollback ()" method will be executed.

The JLupinTransactionManager interface:

public interface JLupinTransactionManager {
       public void begin()    throws Throwable;
       public void commit()   throws Throwable;
       public void rollBack() throws Throwable;
       public  T getSourceTransactionManager() ;
}

The (begin, commit, rollback) methods are self-explanatory - with the help of the getSourceTransactionManager method the source transaction manager can be taken if there is a need for additional activities. The implementation of the foregoing transaction manager might be implemented with the help of the classes provided by Spring framework.

An example:

  1. DataSource Configuration (the dataSource implementation is based on org.apache.commons.dbcp.BasicDataSource):

  2. DataSourceTransactionManager

  3. JLupinTransactionManager

JLupinTransactionManager interface implementation

package com.jlupin.test.transaction.impl;

import com.jlupin.impl.util.JLupinUtil;
import com.jlupin.interfaces.context.service.JLupinServiceContext;
import com.jlupin.interfaces.manager.transaction.JLupinTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class JLupinTransactionManagerImpl implements JLupinTransactionManager{

    private DataSourceTransactionManager dataSourceTransactionManager;

    @Override
    public void begin() throws Throwable {

        TransactionDefinition txDef = new DefaultTransactionDefinition();
        TransactionStatus txStatus = dataSourceTransactionManager.getTransaction(txDef);
        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        jLupinServiceContext.addObject("transactionStatus", txStatus);

    }

    @Override
    public void commit() throws Throwable {
        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        TransactionStatus txStatus = jLupinServiceContext.getObject("transactionStatus");
        dataSourceTransactionManager.commit(txStatus);
    }

    @Override
    public void rollBack() throws Throwable {
        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        TransactionStatus txStatus = jLupinServiceContext.getObject("transactionStatus");
        dataSourceTransactionManager.rollback(txStatus);
    }

    @Override
    public  T getSourceTransactionManager() {
        return (T)dataSourceTransactionManager;
    }

    public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) {
        this.dataSourceTransactionManager = dataSourceTransactionManager;
    }
}

In the foregoing implementation the (id) transaction status as a type of DefaultTransactionDefinition is used. Because the transaction object is stateful and JLupinTransactionManagerImpl is a singleton the transaction status is saved with the help of the JLupinServiceContext object which stores the variable in the thread's local variable so as not to compromise and not to lose the status of the transaction between threads.

How to read the transaction propagation from the system which invokes our system? - namely, do we join to the existing transaction, do we begin new one etc.?

The transactionObject parameter located in the JLupinInputParameter input parameter serves the aforementioned purpose. Because it is "Object" type, it can be freely established between our system and the invoking system. The following shows a basic example (it also includes an example of the transaction's isolation levels).

The system invoking our system sets the Integer type as the isolation propagation level ("switch case" section). The transactionObject object may be any type (all depends on system designer's arrangements)

package com.jlupin.test.transaction.impl;

import com.jlupin.impl.util.JLupinUtil;
import com.jlupin.interfaces.common.to.JLupinInputParameter;
import com.jlupin.interfaces.context.service.JLupinServiceContext;
import com.jlupin.interfaces.manager.transaction.JLupinTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class JLupinTransactionManagerTSImpl implements JLupinTransactionManager {

    private DataSourceTransactionManager dataSourceTransactionManager;

    @Override
    public void begin() throws Throwable {

        DefaultTransactionDefinition txDef = new DefaultTransactionDefinition();

        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        JLupinInputParameter jLupinInputParameter = jLupinServiceContext.getJLupinInputParameter();

        Integer propagation  = (Integer)jLupinInputParameter.getTransactionObject();

        switch (propagation) {
            case 1:
                txDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
                break;
            case 2:
                txDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
                break;
            case 3:
                txDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_NEVER);
                break;
        }

        txDef.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_READ_COMMITTED);

        TransactionStatus txStatus = dataSourceTransactionManager.getTransaction(txDef);
        jLupinServiceContext.addObject("transactionStatus", txStatus);

    }

    @Override
    public void commit() throws Throwable {
        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        TransactionStatus txStatus = jLupinServiceContext.getObject("transactionStatus");
        dataSourceTransactionManager.commit(txStatus);
    }

    @Override
    public void rollBack() throws Throwable {
        JLupinServiceContext jLupinServiceContext = JLupinUtil.getJLupinServiceContext();
        TransactionStatus txStatus = jLupinServiceContext.getObject("transactionStatus");
        dataSourceTransactionManager.rollback(txStatus);
    }

    @Override
    public  T getSourceTransactionManager() {
        return (T)dataSourceTransactionManager;
    }

    public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) {
        this.dataSourceTransactionManager = dataSourceTransactionManager;
    }
}

2)The LOCAL_TRANSACTION_SCOPE transactions type

Diagram 2. Local Transaction Overview

Transactions with the local scope - namely, the transaction is started and finished where data exchange between a transactional systems take place. The choice of the transaction scope depends on the system's characteristics. However, the following consideration is worth reading. The problem lies in the complicated integration system. The following is a real situation:

  1. Request entry - transaction opening
  2. 'Select' execution - data retrieval
  3. Calculations performed using collected data
  4. Data sent to the subsystem A
  5. Data received from the subsystem A (the whole operation of the communication with subsystem A takes about 5 seconds) - write to memory
  6. 'update' execution on database using data retrieved from the subsystem A and from point 2
  7. 'Select' execution - data retrieval
  8. Calculations performed using collected data
  9. Data sent to the subsystem B
  10. Data received from the subsystem B (the whole operation of the communication with subsystem B takes about 3 seconds) - write to memory
  11. 'update' execution on database using data retrieved from the subsystem B and from point 7
  12. Response sent - closing the transaction

Assuming that subsystem A and B is not transactional. Opening one global transaction on selected database records can last at least 8 seconds plus operations processed on the database - say 1 second - gives us 9 seconds on every transaction which may kill the system when we have approx. 30 requests per second. We also may encounter a longer subsystem response time from system A and B. Global transactions seem to be handy and easy in use because we do not need to worry about them. However, in the long run they may cause many problems or even system and database unavailability (long locks and waiting for access to the same data between requests will cause a long waiting queue). There will be a warning on JLNS's website concerning those global transactions.

Description of local transaction managers. Observe the previous situation. To avoid described locks, correct operation order should look as follows:

  1. Request entry - no transaction opening
  2. 'Select' execution - data retrieval (opening and closing of the local transaction) - writes data to memory
  3. Calculations performed using collected data
  4. Data sent to the subsystem A
  5. Data received from the subsystem A (the whole operation of the communication with subsystem A takes about 5 seconds) - write to the memory
  6. Not executing an 'update' on database using data retrieved from the subsystem A and from the second point - see point nr. 12
  7. 'Select' execution - data retrieval (opening and closing of the local transaction) - writes data to memory
  8. Calculations performed using collected data
  9. Data sent to the subsystem B
  10. Data received from the subsystem B (the whole operation of the communication with subsystem B takes about 3 seconds) - write to memory
  11. Not executing an 'update' on database using data retrieved from the subsystem B and from the seventh point - see point nr. 12
  12. The opening of a local transaction, data retrieval from the memory (points nr. 2,5,7,10) an 'update' on the database in a single transaction in order to maintain data consistency, closing the transaction (all takes no more than a second)
  13. Response sent - not closing the transaction ( there is no transaction at this point, there wasn't any transaction at all)

Thus, using the foregoing solution we avoid transactions hanging on the database a maximum of one second instead of nine seconds.

In order to use local transactions we suggest using the org.springframework.transaction.support.TransactionTemplate object. An example of its usage is shown in a link provided at the beginning of this chapter