Étienne Miret
Étienne Miret

Reputation: 6660

TransactionRequiredException thrown by Hibernate inside a Spring transaction

Hibernate is throwing a javax.persistence.TransactionRequiredException inside one of my test methods. But, according to the logs, there is a transaction in progress (created by Spring). Has anyone any idea of what I could be missing?

See the logs:

INFO  org.springframework.test.context.transaction.TransactionalTestExecutionListener - Began transaction (1) for test context [DefaultTestContext@506dd108 testClass = FooTest, testInstance = com.example.FooTest@59b68d78, testMethod = test@FooTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@6001ef4b testClass = FooTest, locations = '{classpath:/appContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@1f05562b]; rollback [true]
INFO  com.example.FooTest - Test started.
INFO  org.springframework.test.context.transaction.TransactionalTestExecutionListener - Rolled back transaction after test execution for test context [DefaultTestContext@506dd108 testClass = FooTest, testInstance = com.example.FooTest@59b68d78, testMethod = test@FooTest, testException = javax.persistence.TransactionRequiredException: Executing an update/delete query, mergedContextConfiguration = [MergedContextConfiguration@6001ef4b testClass = FooTest, locations = '{classpath:/appContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]

My test class is:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/appContext.xml")
@Transactional
public class FooTest {

    private static final Logger logger = LoggerFactory.getLogger(FooTest.class);

    @PersistenceContext
    private EntityManager em;

    @Test
    public void test() {
        logger.info("Test started.");
        em.createQuery("delete from Foo").executeUpdate();
        logger.info("Test finished.");
    }

}

My appContext.xml is:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/jdbc
           http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

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

  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="embeddedDataSource"/>
  </bean>

  <tx:annotation-driven transaction-manager="transactionManager"/>

  <jdbc:embedded-database id="embeddedDataSource">
    <jdbc:script location="classpath:schema.sql" encoding="UTF-8"/>
    <jdbc:script location="classpath:test-data.sql" encoding="UTF-8"/>
  </jdbc:embedded-database>

</beans>

My META-INF/persistence.xml is rather straightforward:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">
  <persistence-unit name="Foo">

    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

    <class>com.example.Foo</class>

  </persistence-unit>
</persistence>

And, finally, the stacktrace (filtered):

javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:71)
    at com.example.FooTest.test(FooTest.java:27)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)

Upvotes: 1

Views: 2336

Answers (2)

&#201;tienne Miret
&#201;tienne Miret

Reputation: 6660

I was using the wrong transaction manager. The solution was to replace:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="embeddedDataSource"/>
</bean>

by

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

Many thanks to M. Deinum for his comment. Had he posted it as an answer, I would have accepted it.

Upvotes: 1

lorinpa
lorinpa

Reputation: 556

I agree that you should be using a JpaTransactionManager. I also believe you need to mark the method transactional:

@Transactional
public void test() {

You can debug it by hard coding the transaction begin and commit in your test. It looks like the exception in pointing to em.createQuery.... (thus hard code the transaction there, if you want to diagnose further).

Hope that helps a little:)

Upvotes: 1

Related Questions