开发者

What's the link between @Transactional and cascading?

开发者 https://www.devze.com 2023-04-05 17:42 出处:网络
There seems to开发者_如何学Go be a link between the presence of the @Transactional annotation on a Spring JUnit test and cascading when persisting/merging a JPA2 entity.

There seems to开发者_如何学Go be a link between the presence of the @Transactional annotation on a Spring JUnit test and cascading when persisting/merging a JPA2 entity.

I don't have the configuration at hand for the moment, but maybe this rings a bell to somebody in here ?


Assume a simple case of JPA entities on three levels: Entity A references an entity of class B and that instance of class B references an instance of class C.

A -> B -> C

Class A does cascading ALL to B. And B does cascading ALL to C. And Class C has an event listener method annotated with @PrePersist and @PreUpdate. It logs a message to prove the cascading made it to there.


Now, modify entity C in some way and ask the entity manager to merge or persist the instance of A. Logically entity C will eventually be persisted or merged also. Because of cascading has been set to ALL from class A to B to C.

When the Spring unit test is not annotated with @Transactional, the log message from the event listener method of class C prints its message. OK.

But when it is annotated with @Transactional, no message at all is printed. And indeed, nothing has been committed to the database for class C. Only for class A. Hence, I conclude the cascading didn't make it from A to C.

Removing the annotation fixes the problem.


Anybody any clue? :-) Logically I would think transactions and cascading are two totally separated matters.


A typical test case configuration:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-beans.xml")
@TransactionConfiguration
@Transactional
public class MyUnitTest {

...

  @Test
  public void testSomething() {}  

...

}

An extract of the Spring xml configuration file - nothing fancy there I think ...

  <context:annotation-config />

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

  <context:component-scan base-package="com.foo.bar"  />

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

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="/META-INF/persistence.xml"/>
    <property name="persistenceUnitName" value="bar" />
  </bean>

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

Extract from persistence.xml

<persistence-unit name="bar" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
      <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/bar" />
      <property name="hibernate.connection.username" value="bar" />
      <property name="hibernate.connection.password" value="pwd" />
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
      <property name="hibernate.hbm2ddl.auto" value="create"/>
      <property name="dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
  </persistence-unit>

Libraries

  • Spring 3.0.6 ORM/CONTEXT/TEST
  • Hibernate 3.6.7.Final
  • JUnit 4.9
  • JPA2


Found it ! An improper entity manager usage was turning bad when transactions were enabled. It wasn't related to the persistence but was done right before it. Causing the persistence to fail in some way.

I implemented a Query result iterator for which an EntityManager was required. I thought I could create it from the EntityManagerFactory of the jpaTemplate.

final EntityManager em = jpaTemplate.getEntityManagerFactory().createEntityManager();
return new QueryIterator<T>(em.createQuery("FROM Foo"));

Obviously not. It seems the EntityManager has to be obtained in a different way. As described underneath.

jpaTemplate.execute(new JpaCallback() {
  @Override
  public Object doInJpa(final EntityManager em) throws PersistenceException {
    return new QueryIterator<T>(em.createQuery("FROM Foo"));
  }
});

Now it all works ok. As it is supposed to be, regardless of transactions being present or not. :-)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号