@Transactional 和级联之间有什么联系?

发布于 2024-12-04 16:56:04 字数 2778 浏览 1 评论 0原文

Spring JUnit 测试中 @Transactional 注释的存在与持久/合并 JPA2 实体时的级联之间似乎存在联系。

我目前手头没有配置,但这也许对这里的某人敲响了警钟?


假设三个级别上的 JPA 实体的简单情况:实体 A 引用类 B 的实体,类 B 的实例引用类 C 的实例

。 B-> C

类 A 将所有级联到 B。B 将所有级联到 C。C 类有一个用 @PrePersist 和 @PreUpdate 注释的事件侦听器方法。它会记录一条消息来证明级联已到达那里。


现在,以某种方式修改实体 C 并要求实体管理器合并或持久化 A 的实例。从逻辑上讲,实体 C 最终也会被持久化或合并。因为从A类到B类再到C类,级联已经设置为ALL。

当Spring单元测试没有使用@Transactional注解时,来自C类的事件监听方法的日志消息将打印其消息。好的。

但是当它用 @Transactional 注释时,根本不会打印任何消息。事实上,没有任何内容被提交到 C 类的数据库中。仅适用于 A 类。因此,我得出结论,级联没有从 A 到 C。

删除注释可以解决问题。


有人有任何线索吗? :-) 从逻辑上讲,我认为事务和级联是两个完全独立的问题。


典型的测试用例配置:

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

...

  @Test
  public void testSomething() {}  

...

}

Spring xml 配置文件的摘录 - 我认为没什么花哨的...

  <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>

摘自 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>

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

There seems to 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尹雨沫 2024-12-11 16:56:04

找到了!当启用事务时,不正确的实体管理器使用会变得很糟糕。这与坚持无关,但在坚持之前就已经完成了。以某种方式导致持久性失败。

我实现了一个需要 EntityManager 的查询结果迭代器。我想我可以从 jpaTemplate 的 EntityManagerFactory 创建它。

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

显然不是。看来 EntityManager 必须以不同的方式获取。如下所述。

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

现在一切正常。正如它应该的那样,无论交易是否存在。 :-)

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. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文