5

I have a little problem with transactions. I use Spring 3.1.1.RELEASE, Spring Data 1.0.3.RELEASE JPA with Hibernate provider. When I start a junit test where is a method annotated with @Transactional it seems fine but when I start a whole application there are no errors but transactions don't work. Here's my configurations and sample code:

applicationContext.xml

<context:annotation-config />
    <context:component-scan base-package="com.sheedo.upload" />
    <jpa:repositories base-package="com.sheedo.upload.repository" />
    <tx:annotation-driven transaction-manager="transactionManager" />

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath*:messages/*.properties</value>
                <value>classpath*:*.properties</value>
            </list>
        </property>
    </bean>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="persistenceUnit" />
        <property name="dataSource" ref="dataSource" />
    </bean>

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

    <bean id="dataSource" class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

persistence.xml

<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect" />
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
        <property name="hibernate.connection.charSet" value="UTF-8" />
        <property name="hibernate.show_sql" value="true" />
    </properties>
</persistence-unit>

UserRepository.java

public interface UserRepository extends CrudRepository<User, Long>{ }

UserServiceImpl.java

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public void addUser(String name, String surname) {
        User u = new User(name, surname);
        userRepository.save(u);
        throw new RuntimeException(); // to invoke a rollback
    }
}

UserServiceTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/META-INF/spring/root-context.xml" })
public class UserServiceTest {

    Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private UserService userService;

    @Test
    public void testUserAdd() {
        userService.addUser("John", "Doe");
    }

}

In this case of JUnit test, transaction doesn't work event though the service method is annotated with @Transactional. When I add this annotation to testUserAdd() method I get this in console:

2012-05-17 11:17:54,208 INFO [org.springframework.test.context.transaction.TransactionalTestExecutionListener] - Rolled back transaction after test execution for test context [[TestContext@23ae2a testClass = UserRepositoryTest, testInstance = com.sheedo.upload.repository.UserRepositoryTest@7f52c1, testMethod = testUserAdd@UserRepositoryTest, testException = java.lang.RuntimeException, mergedContextConfiguration = [MergedContextConfiguration@111fd28 testClass = UserRepositoryTest, locations = '{classpath:/META-INF/spring/root-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]

which is correct I suppose. So, how can be possible that @Transactional annotation works only in Junit test class, but not in others spring beans ?

My theory is that SpringJUnit4ClassRunner somehow provides this transaction. Have I something wrong in my spring configuration that transactions don't work in my app but only in Junit test classes? Something missing in appContext?

Edit: log:

2012-05-17 12:46:10,770 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2012-05-17 12:46:10,770 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@e4080] for JPA transaction
2012-05-17 12:46:10,979 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl@e4080] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@1f87491] does not support JDBC Connection retrieval
Hibernate: insert into user (name, surname) values (?, ?)
2012-05-17 12:46:11,062 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Initiating transaction commit
2012-05-17 12:46:11,062 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Committing JPA transaction on EntityManager [org.hibernate.ejb.EntityManagerImpl@e4080]
2012-05-17 12:46:11,142 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Closing JPA EntityManager [org.hibernate.ejb.EntityManagerImpl@e4080] after transaction
2012-05-17 12:46:11,142 DEBUG [org.springframework.orm.jpa.EntityManagerFactoryUtils] - Closing JPA EntityManager
jantobola
  • 688
  • 2
  • 10
  • 28
  • How do you diagnose that transactions "don't work"? – JB Nizet May 17 '12 at 09:45
  • When I debug this transactional method in UserService, it saved entity into the database after "userRepository.save(user)" even though the method finished with exception. And I have no log in console that transaction began. – jantobola May 17 '12 at 09:55
  • Enable the "org.springframework.transaction" loggers.Post the same. This will help in further analysis. – Ahamed Mustafa M May 17 '12 at 10:13
  • all I got is this in startup: `2012-05-17 12:41:10,386 DEBUG [org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] - Adding transactional method 'addUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''` no log when I call the method.. – jantobola May 17 '12 at 10:43
  • ok, I added `log4j.logger.org.springframework.orm.jpa=debug` and I got something when method was called. Look at my edit in main post. – jantobola May 17 '12 at 10:50
  • You log says that a new transaction is getting created for `save` method.If I am not wrong, your log should have said `Creating new transaction with name [...UserServiceImpl.addUser]`. – Ahamed Mustafa M May 17 '12 at 13:11
  • yes, but what does mean this: `2012-05-17 12:46:10,979 DEBUG [org.springframework.orm.jpa.JpaTransactionManager] - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl@e4080] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@1f87491] does not support JDBC Connection retrieval` ? – jantobola May 17 '12 at 14:15
  • It committed changes, but exception was thrown. Why there isn't a rollback ? – jantobola May 17 '12 at 14:22
  • 1
    `Not exposing JPA transaction..` should not be a problem.Describe the environment in use (server, hibernate version etc).Log before `2012-05-17 12:46:10,770` may give some more info.Try additional as mentioned in http://stackoverflow.com/questions/9051721/declarative-transactions-transactional-doesnt-work-with-repository-in-sprin – Ahamed Mustafa M May 17 '12 at 20:20
  • Thank you so much sir. Adding `` into servlet context helped. – jantobola May 17 '12 at 20:57

1 Answers1

2

I had exactly the same problem. Also, read the solution of adding a <tx:annotation-driven/> tag in my web configuration (spring-servlet.xml, instead of applicationContext.xml), and worked for me.

But I did not consider that a good solution, so I tried to understand why that was happening...

And well, it turned out that a <context:component-scan> tag I had in my spring-servlet.xml was also including the @Service classes in its scan (the base-package specification was too general). This was weird because I had an include-filter in place referring to the @Controller annotation... but anyway, it seems that the application context for the web layer was the one creating the @Service instances instead of the application context created for applicationContext.xml --which is the one defining the business layer--, and as the former didn't have transactionality enabled... I didn't have any transactions.

Solution (good one): better (more specific) component-scan configuration at spring-servlet.xml

Daniel Fernández
  • 7,335
  • 2
  • 30
  • 33
  • Had the same problem. Web app context was loading wrong beans even though include filters were defined. And obviously there was no in the web app context. Inspect your logs for when the spring-servlet.xml (or whatever you named your web layer applicationcontext) loads and look for this statement in the log: "Bean factory for WebApplicationContext" - you can search through those beans to see if the web app context loaded it. – dustin.schultz May 14 '14 at 17:17