JTA 事务提交太早,使用约束时失败
我们有一个在 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
大多数属性(如 firstName
和 lastName
)都用注释@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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当查询发生时,您的实体可能会自动刷新。根据 JavaDocs 的
FlushModeType
:基本上,如果您执行查询,并且任何未提交的实体(尚未设置所有成员)都有资格成为该查询的结果,那么持久性实现必须刷新将它们写入数据库(或执行具有同等效果的操作),从而导致抛出异常,因为可为空约束无效。对我来说,这似乎也有点不直观。如果我们没有在应用程序中遇到非常相似的问题,我就不会知道这一点。
听起来您只想使用
EntityManager.setFlushMode()
或Query.setFlushMode( )
将刷新模式设置为COMMIT
。Your entities are probably getting auto-flushed when queries are occurring. According to the JavaDocs for
FlushModeType
: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()
orQuery.setFlushMode()
to set the flush mode toCOMMIT
.