JTA 事务提交太早,使用约束时失败

发布于 2024-11-06 21:24:12 字数 1587 浏览 3 评论 0原文

我们有一个在 Glassfish 3.1 中运行的 Java EE 应用程序,其中我们的 JPA 模型(使用 EclipseLink)组织如下:

Customer
 -> String firstName
 -> String lastName
 -> Address adress
 -> List<Attribute> attributes
 -> Int age

Address
-> String street
-> Int zip
-> String city

Attribute
 -> String code
 -> String name

大多数属性(如 firstNamelastName)都用注释@Column(nullable=false)。现在我们这样做了:

@Stateless
public class CustomerController {

  @PersistenceContext(unitName = "CustomerService")
  private EntityManager em;

  @EJB
  private AttributeController attributeController;

  public String createCustomer() {
    Customer customer = new Customer();
    customer.firstName = "Hans";
    customer.lastName = "Peter";

    customer.address = new Address();
    customer.adress.street = ...

    customer.attributes = new ArrayList<Attribute>();
    customer.attributes.add(attributeController.getByCode("B"));
    customer.attributes.add(attributeController.getByCode("T"));

    customer.age = 27;

    em.persist(customer);
  }
}

这适用于像上面这样的小类,但是我们现在引入了更多与客户相关的对象,例如带有 @OneToMany 的属性,并且从其他 @EJB 加载 就像 attributeController 一样。

对于“大”模型,现在看来事务是在仍在加载相关对象的过程中提交的,因为我们得到一个错误:“age”列中的空值违反了非空约束。由于我们使用 JTA 容器管理事务并且没有将 @TransactionAttribute 设置为除默认 REQUIRED 之外的其他内容,因此我们无法直接控制事务,并且不知道出了什么问题这里。

在提交之前是否可以使用一定数量的“工作单元”?我们加载相关对象是否错误?我们还犯过其他重大错误吗?

一旦我们省略 nullable=false 约束,一切都会正常工作......

We have a Java EE application running in Glassfish 3.1, where we have our JPA models (using EclipseLink) organized like this:

Customer
 -> String firstName
 -> String lastName
 -> Address adress
 -> List<Attribute> attributes
 -> Int age

Address
-> String street
-> Int zip
-> String city

Attribute
 -> String code
 -> String name

Most of the attributes like firstName and lastName are annotated with @Column(nullable=false). Now we do:

@Stateless
public class CustomerController {

  @PersistenceContext(unitName = "CustomerService")
  private EntityManager em;

  @EJB
  private AttributeController attributeController;

  public String createCustomer() {
    Customer customer = new Customer();
    customer.firstName = "Hans";
    customer.lastName = "Peter";

    customer.address = new Address();
    customer.adress.street = ...

    customer.attributes = new ArrayList<Attribute>();
    customer.attributes.add(attributeController.getByCode("B"));
    customer.attributes.add(attributeController.getByCode("T"));

    customer.age = 27;

    em.persist(customer);
  }
}

This works for small classes like the one above, but we have now introduced more objects which are related to the customer like attributes with a @OneToMany and are loaded from other @EJBs like the attributeController.

For "big" models it now seems like a transaction is commited right in the process of still loading related objects, since we get a ERROR: null value in column "age" violates not-null constraint. Since we use JTA container managed transactions and have not set @TransactionAttribute to something other than the default REQUIRED, we do not control the transaction directly and have no idea what is going wrong here.

Is there a certain amount of "units of work" that can be oben before a commit takes place? Are we loading the related objects wrong? Have we made some other major mistake?

As soon as we omit the nullable=false constraints, everything works fine...

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

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

发布评论

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

评论(1

素衣风尘叹 2024-11-13 21:24:12

当查询发生时,您的实体可能会自动刷新。根据 JavaDocs 的 FlushModeType

当在事务中执行查询时,如果在查询对象上设置了 FlushModeType.AUTO,或者持久性上下文的刷新模式设置为 AUTO >(默认)并且尚未为查询对象指定刷新模式设置,持久性提供者负责确保对持久性上下文中所有实体的状态进行所有更新 这可能会影响结果查询对于查询的处理是可见的。持久性提供程序实现可以通过将这些实体刷新到数据库或通过一些其他方式来实现这一点。如果设置了FlushModeType.COMMIT,则未指定对查询时持久性上下文中的实体进行更新的效果。


基本上,如果您执行查询,并且任何未提交的实体(尚未设置所有成员)都有资格成为该查询的结果,那么持久性实现必须刷新将它们写入数据库(或执行具有同等效果的操作),从而导致抛出异常,因为可为空约束无效。对我来说,这似乎也有点不直观。如果我们没有在应用程序中遇到非常相似的问题,我就不会知道这一点。

听起来您只想使用 EntityManager.setFlushMode()Query.setFlushMode( ) 将刷新模式设置为COMMIT

Your entities are probably getting auto-flushed when queries are occurring. According to the JavaDocs for FlushModeType:

When queries are executed within a transaction, if FlushModeType.AUTO is set on the Query object, or if the flush mode setting for the persistence context is AUTO (the default) and a flush mode setting has not been specified for the Query object, the persistence provider is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query. The persistence provider implementation may achieve this by flushing those entities to the database or by some other means. If FlushModeType.COMMIT is set, the effect of updates made to entities in the persistence context upon queries is unspecified.

Basically, if you do a query, and any of your uncommitted entities (which don't have all their members set yet) would be eligible to be a result of that query, then the persistence implementation must flush them to the database (or do something with equivalent effect), thus causing an exception to be thrown because the nullable constraints are invalid. It seems a little unintuitive to me, too. I wouldn't have known about it if we hadn't run into a very similar issue in our application.

It sounds like you just want to use EntityManager.setFlushMode() or Query.setFlushMode() to set the flush mode to COMMIT.

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