Search code examples
springscalatransactionalspring-transactions

Spring Transaction with Scala


I've tried to use some beans programmed in scala in my Spring (v 3.1) web application and have encountered strange problems when it comes to transactions.

I've setup a small but complete Spring application in Roo to show my problem. To be honest, I don't have a clue why it is not working...

project --topLevelPackage com.app
jpa setup --provider ECLIPSELINK --database DERBY_EMBEDDED
entity jpa --class com.app.MyEntity
field string --fieldName title

Then I created the interfaces in java

package com.app;
public interface IScalaMainClass {
 void doSomething();
}

package com.app;
public interface IScala1 {
 void someMethod();
}

package com.app;
public interface IScala2 {
 void someMethod();
}

The implementations are created in Scala where Scala1 persists MyEntity and Scala2 makes a DELETE query:

package com.app

import org.springframework.transaction.annotation.Transactional
import org.springframework.stereotype.Repository
import org.springframework.beans.factory.annotation.Autowired

@Repository class ScalaMainClass extends com.app.IScalaMainClass {
 @Autowired var bean1: IScala1 = null
 @Autowired var bean2: IScala2 = null

 @Transactional def doSomething() = {
  bean1.someMethod()
  bean2.someMethod()
 }
}


package com.app

import org.springframework.stereotype.Repository
import javax.persistence.{EntityManager, PersistenceContext}

@Repository class Scala1 extends com.app.IScala1 {
 @PersistenceContext var em: EntityManager = null

 def someMethod = {
  val e = new MyEntity
  em persist e
 }
}


package com.app

import org.springframework.transaction.annotation.Transactional
import org.springframework.stereotype.Repository
import javax.persistence.{EntityManager, PersistenceContext}

@Repository class Scala2 extends com.app.IScala2 {
 @PersistenceContext var em: EntityManager = null

 @Transactional def someMethod = {
  val q = em createQuery "DELETE FROM MyEntity"
  q executeUpdate
 }

}

Finally the test in Java is created which executes the IScalaMainClass:

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.beans.factory.annotation.Autowired;

import org.junit.runner.RunWith;
import org.junit.Test;
import org.junit.Assert;

import com.app.IScalaMainClass;

@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback = false)
public class AppTest {
 @Autowired private IScalaMainClass smc;

 @Test public void testIt() {
  smc.doSomething();
 }
}

If you now add the scala dependencies to pom.xml and do "mvn test" you will receive

javax.persistence.TransactionRequiredException: 
Exception Description: No transaction is currently active
...

Solution

  • You need to be very careful when you look to Spring support for Transaction and other Aspect-oriented features.

    • Autowiring an immutable field is not possible: autowiring is a pattern which sets up dependency after initialization, val is an immutable property which cannot be changed after initialization

    • As you can read in the documentation, Spring AOP support is not based on bytecode weaving and it is purely implemented in Java: http://static.springsource.org/spring/docs/3.0.x/reference/aop.html

    This means that only beans which are created through Spring IoC could benefit of Spring AOP support.

    If you look in the introduction of the chapter you can read:

    AOP is used in the Spring Framework to...

    ... provide declarative enterprise services, especially as a replacement for EJB declarative services. The most important such service is declarative transaction management.

    So if your bean is not created through Spring, you will not have spring transaction support available.