-->

JPA与JTA:坚持实体与合并级联子实体(JPA with JTA: Persist entity

2019-06-25 11:35发布

我有以下实体类的双向一个一对多的关系:

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

当坚持我想要的相关产品订单的实体被持久化,太客户端实体(如自己的外键“父”的客户可能已经更新)。

当然,所有需要CASCADE选项都在客户端设置。 但是,如果一个新创建的客户端持续了第一次这是行不通的,同时参照现有的产品订单在这种情况下:

  1. 产品订单“1”被创建和保持。 工作良好。
  2. 客户端“2”被创建和产品顺序“1”被添加到它的产品订单列表。 然后,它仍然存在。 不工作。

我试了apporaches,但它们都没有表现出预期的结果。 请参阅下面的结果。 我在这里阅读所有相关的问题,但他们并没有帮助我。 我将EclipseLink 2.3.0,纯JPA 2.0注释和JTA作为一个Apache德比(JavaDB之外)与内部存储器DB在GlassFish 3.1.2事务类型。 实体关系由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
}

ProductOrder:

@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);
}

结果:

创建()立刻引发此异常:

警告:EJB ClientFacade方法public void javaee6test.beans.AbstractFacade.create(java.lang.Object中)javax.ejb.EJBException异常调用期间发生系统异常:事务终止...

javax.transaction.RollbackException:由交易造成的标记为回滚。 ...

产生的原因:异常[的EclipseLink-4002](Eclipse持久服务 - 2.3.0.v20110604-r9504):org.eclipse.persistence.exceptions.DatabaseException内部异常:java.sql.SQLIntegrityConstraintViolationException:国有包换被放弃,因为它会造成在一个独特的或主键约束或通过在“PRODUCTORDER”定义的“SQL120513133540930”识别的唯一索引重复键值。 错误代码:-1调用:(?,?)INSERT INTO PRODUCTORDER(ID,CLIENT_ID)值绑定=> [2个参数绑定]查询:InsertObjectQuery(javaee6test.model.ProductOrder [ID = 1])...

java.sql.SQLIntegrityConstraintViolationException:由此造成的语句已放弃,因为它会造成对“PRO-DUCTORDER”定义的唯一或主键约束或“SQL120513133540930”标识的唯一索引中的重复的键值。 ...

org.apache.derby.client.am.SqlException:由此造成的语句已放弃BE-因为它会在唯一或主键CON-straint或定义“SQL120513133540930”标识的唯一索引中导致重复键值“生产方-DER”。

我不明白这一点例外。 编辑()工作正常。 但我想,产品订单,在其创建时间添加到客户端,所以这是不够的。

方法2)合并()只

更改通用持久门面:

// 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);
}

结果:

在创建()时,的EclipseLink记录输出说道:

细:INSERT INTO CLIENT(ID,NAME,ADDRESS_ID)值绑定=> [3个参数结合](,?,??)

但对产品订单表号“UPDATE”。 因此,这种关系是不成立的。 再次,编辑(),而另一方面,做工精细。

Apporach 3)身份证GenerationType.IDENTITY两个实体类型

更改客户端和品阶等级:

...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...

结果:

在创建()时,的EclipseLink记录输出说道:

细:(?,?)INSERT INTO CLIENT(NAME,ADDRESS_ID)值绑定=> [2个参数绑定]

精细:值IDENTITY_VAL_LOCAL()

细:(?,?)INSERT INTO PRODUCTORDER(ORDERDATE,CLIENT_ID)值绑定=> [2个参数绑定]

精细:值IDENTITY_VAL_LOCAL()

因此,不是estabilshing以添加到客户端的列表中的产品订单有关系,创建一个新的prodcut秩序的实体和持久化(!),并以该实体的关系estabilshed。 同样在这里,编辑()工作正常。

Apporach 4)方法(2)和(3)组合的

结果:同方法(2)。

我的问题是: 有没有办法实现上述场景? 怎样才可以archieved? 我想留在JPA(无特定供应商的解决方案)。

Answer 1:

嗨,我今天有同样的问题,我问到与此电子邮件OpenJPA的邮件列表:

你好。 我有一个问题,插入和更新同一实体的引用。

我试着去插入一个新的对象(考试),有另一个对象(人)的引用,并在同一时间,我想更新Person对象的属性(生日)。 更新从未发生过,虽然我设置的CascadeType为ALL。 这个作品的唯一方法是做一个坚持,之后合并操作。 这是正常的吗? 我需要改变些什么?

我不喜欢“手动更新”使用Person对象合并,因为我不知道用户想要多少个对象(考试的子对象),以更新的理念。

实体:

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();       
   }

}

我一定要使用合并方法对每个子对象?

得到的回答是这样的:

它看起来像你的问题是,考试是新的,但人是现有的级联和持久化操作时,现有的实体被忽略。 我相信这是按预期工作。

只要你的人际关系被设置为CascadeType.ALL,你可以随时更改您的em.persist(考试); 到em.merge(考试);. 这会照顾坚持新的考试,它也将级联合并呼叫的人。

谢谢,里克·


我希望这可以帮助你。



Answer 2:

@SputNick,我解决它的步骤描述below.I我要使用你的代码片段展示我做什么,

你有客户端实体- @一对多ProductOrder实体- @多对一 。我将使用

// Called when pressing "save" on the "edit..." 
public void edit(T entity) {
 getEntityManager().merge(entity);}

对于持久性,因为它可以给我的灵活性,以及​​保存为更新。

要创建客户端和相应的产品订单使用

client.set..(some property to be saved);

productOrder.set..(some property to be saved);

//设置productOrder客户端

List<ProductOrder> order = new ArrayList<>();

client.setOrders(order); //注意,这预计要传递一个列表

//设置客户端的productOrder

productOrder.setClient(productOrder);

.edit(client) or .edit(productOrder)

请注意,这两种方式均能够与您的更改更新两个实体表。

虽然晚了,希望这能帮助别人



Answer 3:

请使用您ProductOrder实体@JoinColumn注解,请参见下文。

   @Entity
   public class ProductOrder implements Serializable, ChildEntity {
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @ManyToOne // owning side
   @JoinColumn(name = "PRODUCTORDER_ID", referencedColumnName = "ID")
   //write your database column names into name and referencedColumnName attributes.
   private Client client;

   // other fields, getters and setters
   }


Answer 4:

确保您设置关系的双方,你不能只是增加了以客户端,您还需要设置订单的客户。 你也需要合并已更改这两个对象。 如果你只是合并的客户,那么订单的客户将不会被合并(虽然你的级联你使其合并)。

坚持不工作,如坚持要求作为对象持久化是持久化上下文正确的,也就是不引用脱离的对象,它必须引用管理对象。

你的问题来自于你卸下对象。 如果您未剥离的对象,你不会有同样的问题。 通常在JPA您将创建一个EntityManager,查找/查询您的对象,编辑/坚持他们,调用commit。 没有合并是必需的。



文章来源: JPA with JTA: Persist entity and merge cascaded child entities