我与以下实体类具有双向一对多关系:
0 或 1 个客户 <-> 0 个或多个产品订单
当持久化客户端实体时,我希望关联的产品订单实体也被持久化(因为它们到“父”客户端的外键可能已更新)。
当然,所有必需的 CASCADE 选项都在客户端设置。但是,如果在引用现有产品订单时第一次持久化新创建的客户端,则它不起作用,如在这种情况下:
- 产品订单“1”已创建并保留。工作正常。
- 已创建客户“2”并将产品订单“1”添加到其产品订单列表中。然后它被坚持。不起作用。
我尝试了几个方法,但没有一个显示出预期的结果。请参阅下面的结果。我在这里阅读了所有相关问题,但它们对我没有帮助。我在 GlassFish 3.1.2 上的 Apache Derby (JavaDB) 内存数据库上使用 EclipseLink 2.3.0、纯 JPA 2.0 注释和 JTA 作为事务类型。实体关系由 JSF GUI 管理。对象级关系管理有效(除了持久化),我使用 JUnit 测试对其进行了测试。
方法 1)“默认”(基于 NetBeans 类模板)
客户:
@Entity
public class Client implements Serializable, ParentEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy = "client", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},
fetch= FetchType.LAZY)
private List<ProductOrder> orders = new ArrayList<>();
// other fields, getters and setters
}
产品订单:
@Entity
public class ProductOrder implements Serializable, ChildEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne // owning side
private Client client;
// other fields, getters and setters
}
通用持久化外观:
// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
getEntityManager().persist(entity);
}
// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
getEntityManager().merge(entity);
}
结果:
create() 立即抛出此异常:
Warning: A system exception occurred during an invocation on EJB ClientFacade method public void javaee6test.beans.AbstractFacade.create(java.lang.Object) javax.ejb.EJBException: Transaction aborted ...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback. ...
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The state-ment was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL120513133540930' defined on 'PRODUCTORDER'. Error Code: -1 Call: INSERT INTO PRODUCTORDER (ID, CLIENT_ID) VALUES (?, ?) bind => [2 parameters bound] Query: InsertObjectQuery(javaee6test.model.ProductOrder[ id=1 ]) ...
Caused by: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL120513133540930' defined on 'PRO-DUCTORDER'. ...
Caused by: org.apache.derby.client.am.SqlException: The statement was aborted be-cause it would have caused a duplicate key value in a unique or primary key con-straint or unique index identified by 'SQL120513133540930' defined on 'PRODUCTOR-DER'.
我不明白这个异常(exception)。编辑()工作正常。但是我想在创建时向客户添加产品订单,所以这是不够的。
方法 2)仅合并()
Generic Persistence Facade 的变化:
// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
getEntityManager().merge(entity);
}
// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
getEntityManager().merge(entity);
}
结果:
在 create() 上,EclipseLink 日志输出显示:
Fine: INSERT INTO CLIENT (ID, NAME, ADDRESS_ID) VALUES (?, ?, ?) bind => [3 parameters bound]
但产品订单表上没有“更新”。因此,关系不成立。同样,另一方面,edit() 工作正常。
方法 3) 两种实体类型上的 Id GenerationType.IDENTITY
客户和产品订单类别的变化:
...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
结果:
在 create() 上,EclipseLink 日志输出显示:
Fine: INSERT INTO CLIENT (NAME, ADDRESS_ID) VALUES (?, ?) bind => [2 parameters bound]
Fine: values IDENTITY_VAL_LOCAL()
Fine: INSERT INTO PRODUCTORDER (ORDERDATE, CLIENT_ID) VALUES (?, ?) bind => [2 parameters bound]
Fine: values IDENTITY_VAL_LOCAL()
因此,不是建立与添加到客户列表中的产品订单的关系,而是创建并保存一个新的产品订单实体(!),并建立与该实体的关系。同样在这里,edit() 工作正常。
方法 4) 方法 (2) 和 (3) 相结合
结果:与方法(2)相同。
我的问题是:有什么办法可以实现上面描述的场景吗?如何归档?我想继续使用 JPA(没有特定于供应商的解决方案)。
最佳答案
您好,我今天遇到了同样的问题,我用这封电子邮件询问 openJPA 邮件列表:
嗨。我在同一实体中插入和更新引用时遇到问题。
我正在尝试插入一个新对象(Exam),该对象引用了另一个对象(Person),同时我想更新 Person 对象的属性(birthDate)。尽管我将 CascadeType 设置为 ALL,但更新从未发生。唯一可行的方法是进行持久化,然后进行合并操作。这是正常的吗?我必须改变什么吗??
我不喜欢在 Person 对象中使用合并进行“手动更新”的想法,因为我不知道用户想要更新多少对象(Exam 的子对象)。
实体:
public class Exam{
@ManyToOne(cascade= CascadeType.ALL)
@JoinColumn(name = "person_id")
public Person person;
......
}
public class Person{
private Date birthDate;
@OneToMany(mappedBy = "person")
private List<Exam> exams
.......
}
public class SomeClass{
public void someMethod(){
exam = new Exam()
person.setBirthDate(new Date());
exam.setPerson(person);
someEJB.saveExam(exam);
}
}
public class someEJB(){
public void saveExam(Exam exam){
ejbContext.getUserTransaction().begin();
em.persist(exam);
//THIS WORKS
em.merge(exam.getPerson());
ejbContext.getUserTransaction().commit();
}
}
是否必须对每个子对象都使用 MERGE 方法?
答案是这样的:
看起来你的问题是考试是新的,但人是 现有实体和现有实体在级联持久性时被忽略 手术。我相信这会按预期工作。
只要你的关系设置为 CascadeType.ALL,你总是可以 改变你的 em.persist(exam); em.merge(exam);.那会照顾 坚持新的考试,它也会将合并调用级联到 人。
谢谢, 瑞克
希望对您有所帮助。
关于java - JPA 与 JTA : Persist entity and merge cascaded child entities,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11029260/