Blog Archives

Spring commits on NOT_SUPPORTED propagation level

If you are working with spring and executing a DML query inside a transaction which has a propagation level of NOT_SUPPORTED, you might get surprised that the DML query gets committed despite the fact that the propagation level is NOT_SUPPORTED.

Read the rest of this entry

Execute before or after a spring transactional method

Spring provides a mechanism through which you can plugin some code which gets executed after the transaction is complete. We need to provide an implementation of the org.springframework.transaction.support.TransactionSynchronization interface which can be registered using TransactionSynchronizationManager. This could be useful in scenario where your code does not have a control over when the transaction starts and ends. For e.g. Your service code always joins an existing transaction started by the calling method (using REQUIRED PROPAGATION LEVEL).

Read the rest of this entry

Spring ignores CGLib subclasses when detecting @Transactional

Starting with spring version 3.0.4, cglib sub-classes are ignored when the spring code analyzes a class for @Transactional annotations.

Below is the method from AbstractFallbackTransactionAttributeSource class which determines if the transaction attribute is present on the method invocation. If the transaction attribute is present spring wraps the class around a proxy.

private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
		// Don't allow no-public methods as required.
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
			return null;
		}

		// Ignore CGLIB subclasses - introspect the actual user class.
               Class<?> userClass = ClassUtils.getUserClass(targetClass);

		// The method may be on an interface, but we need attributes from the target class.
		// If the target class is null, the method will be unchanged.
		Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
		// If we are dealing with method with generic parameters, find the original method.
		specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

		// First try is the method in the target class.
		TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
		if (txAtt != null) {
			return txAtt;
		}

		// Second try is the transaction attribute on the target class.
		txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
		if (txAtt != null) {
			return txAtt;
		}

		if (specificMethod != method) {
			// Fallback is to look at the original method.
			txAtt = findTransactionAttribute(method);
			if (txAtt != null) {
				return txAtt;
			}
			// Last fallback is the class of the original method.
			return findTransactionAttribute(method.getDeclaringClass());
		}
		return null;
	}

I traced this out after I found that creation of the application context for my junit test cases failed with spring 3.0.4 jars but passed when spring 3.0.3 jars were used. My spring context xml file for the junit test cases looked similar to

    <context:component-scan base-package="sample">
        <context:exclude-filter type="regex" expression="sample.SomeService" />
    </context:component-scan>

    <bean id = "entityBasePackages" class = "java.lang.String">
        <constructor-arg value = "sample"/>
    </bean>

The junit testcase wanted to mock the class SomeService which contained @Transactional annotations hence it was excluded from the component scan.

@Service
public class SomeService {

    @Transactional
    public void transactionalMethod() {
        System.out.println("Transactional Method");
        //perform some database operations
    }
}

When the test case was executed with spring 3.0.4 jars it failed with the below error

Caused by: java.lang.IllegalArgumentException: Can not set sample.SomeService field sample.MyService.someService to $Proxy26
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
	at java.lang.reflect.Field.set(Field.java:657)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:503)
	... 43 more

The application context creation failed because spring ignored the cglib subclass which was created by EasyMock for SomeService class. Hence a JDK proxy over a CGlib proxy was created which lead to type cast failure as indicated from the error above.

The resolution to the above failure could be any of the two options below
1. Creating an interface for SomeService and replacing the class reference variable in MyService class. This would make the type cast succeed because the JDK proxy created by spring would now include the new interface in the list of proxy interfaces.

2. Remove the @Transactional annotations from the SomeService class so that spring does not create any proxies.

Supporting Custom Isolation Levels With JPA

This post provides a workaround for supporting custom isolation levels with JPA. If you use the HibernateJpaDialect (provided by spring)  and try to set an isolation level different from the default one you would get an exception “Standard JPA does not support custom isolation levels – use a special JpaDialect”

The above error would be seen when your configuration is as below

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" lazy-init="true">
   <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          lazy-init="true">
        <property name="persistenceUnitName" value="spmsoftwareunit"/>
        <property name="persistenceXmlLocation" value="classpath:com/spmsoftware/sampleapp/conf/persistence.xml"/>
        <property name="dataSource" ref="oracleDataSourceFactoryBean"/>
        <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.id.new_generator_mappings">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                <prop key="hibernate.jdbc.batch_size">3</prop>
                <prop key="hibernate.jdbc.batch_versioned_data">true</prop>
                <prop key="hibernate.jdbc.factory_class">com.spmsoftware.sampleapp.persistence.OracleBatchingBatcherFactory</prop>
                <prop key="hibernate.hbm2ddl.auto">none</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

and the transaction is applied as

@Transactional(isolation=Isolation.SERIALIZABLE, propagation=Propagation.REQUIRED)

WorkAround

We can extend HibernateJpaDialect to modify the connection instance before the transaction starts

import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import java.sql.Connection;
import java.sql.SQLException;

public class HibernateExtendedJpaDialect extends HibernateJpaDialect {

    private Logger logger = LoggerFactory.getLogger(HibernateExtendedJpaDialect.class);

    /**
     * This method is overridden to set custom isolation levels on the connection
     * @param entityManager
     * @param definition
     * @return
     * @throws PersistenceException
     * @throws SQLException
     * @throws TransactionException
     */
    @Override
    public Object beginTransaction(final EntityManager entityManager,
            final TransactionDefinition definition) throws PersistenceException,
            SQLException, TransactionException {
        Session session = (Session) entityManager.getDelegate();
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            getSession(entityManager).getTransaction().setTimeout(definition.getTimeout());
        }

        entityManager.getTransaction().begin();
        logger.debug("Transaction started");

        session.doWork(new Work() {

            public void execute(Connection connection) throws SQLException {
                logger.debug("The connection instance is {}", connection);
                logger.debug("The isolation level of the connection is {} and the isolation level set on the transaction is {}",
                        connection.getTransactionIsolation(), definition.getIsolationLevel());
                DataSourceUtils.prepareConnectionForTransaction(connection, definition);
            }
        });

        return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());
    }

}

The above implementation takes care of setting the isolation level specified on the transaction definition by calling DataSourceUtils.prepareConnectionForTransaction(connection, definition);

Since the isolation level is changed on the connection instance, resetting it is important. This would have to be done in the data source since the connection gets closed as soon as the transaction is committed. There is no way to intercept the JpaTransactionManager code flow to reset the isolation level after commit is done.
By decorating the data source and setting the isolation level before the connection is given by the connection pool, one can achieve resetting of the isolation level on the connection instance to default (which in most of the cases is read committed).

Spring transaction behavior in private & internal methods

This post is to detail on the spring behavior when @Transactional annotations are applied on private methods or when transactional methods are called from within a method in the same class.

Transactional Private Methods

public class Bean {
  public void doStuff() {
     doPrivateStuff();
  }
  @Transactional
  private void doPrivateStuff() {

  }
}

...

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

@Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them.
As per the Spring manual

Method visibility and @Transactional
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ if you need to annotate non-public methods.

Calling internal transactional methods

public class Bean {
   public void doStuff() {
     doTransactionStuff();
   }

   @Transactional
   public void doTransactionStuff() {

   }
}

This will open a transaction:

Bean bean = (Bean)appContext.getBean("bean");
bean.doTransactionStuff();

This will not:

Bean bean = (Bean)appContext.getBean("bean");
bean.doStuff();

As per the Spring manual
Note: In proxy mode (which is the default), only ‘external’ method calls coming in through the proxy will be intercepted. This means that ‘self-invocation’, i.e. a method within the target object calling some other method of the target object, won’t lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

Consider the use of AspectJ mode if you expect self-invocations to be wrapped with transactions as well. In this case, there won’t be a proxy in the first place; instead, the target class will be ‘weaved’ (i.e. its byte code will be modified) in order to turn @Transactional into runtime behavior on any kind of method.

Summary

The @Transactional annotation defines a transaction boundary, and Spring will start/rollback/commit the transaction every time a @Transactional-annotated method is entered or exited.
If you execute a @Transactional-annotated method, and that method then calls another method in another class, and that one calls another one, which eventually does some database work, then you’re still inside the transaction.