java - JPA 与 JTA : Persist entity and merge cascaded child entities

标签 java jpa jpa-2.0 entity-relationship jta

我与以下实体类具有双向一对多关系:

0 或 1 个客户 <-> 0 个或多个产品订单

当持久化客户端实体时,我希望关联的产品订单实体也被持久化(因为它们到“父”客户端的外键可能已更新)。

当然,所有必需的 CASCADE 选项都在客户端设置。但是,如果在引用现有产品订单时第一次持久化新创建的客户端,则它不起作用,如在这种情况下:

  1. 产品订单“1”已创建并保留。工作正常。
  2. 已创建客户“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/

相关文章:

java - Java中的@override和Python中的@decorator的区别

java堆大小增加

java - 使用 SpannableString 长按 TextView 时出现 NullPointerException

java - hibernate自动类型转换

java - JPA 为每个项目选择最新实例

java - 从 json 中删除多余的空格

java - 在 JPA/Hibernate 中建模二对多关系

mysql - 由 : java. sql.SQLException 引起:字段 'customerNumber' 没有默认值 - hibernate 和 jpa

jpa - Spring Data JPA Projection嵌套列表投影接口(interface)

java - 在 Oracle 上使用 Hibernate 的死锁事务