在使用 jpa 的多对多关系时,如何避免出现 ValidationException?

How do I avoid getting ValidationException when using a manytomany relation with jpa?

我随机得到以下异常:org.eclipse.persistence.exceptions.ValidationException Exception Description: The attribute [id] of class [domain.Person] is mapped to a primary key column in the database. Updates are not allowed.

异常主要在执行"deleteAllPersons()"时抛出。当必须删除的 2 个实体之间存在 link 时会发生错误,并且在我的情况下似乎发生在被删除的第二个人身上。

同样值得注意的是,错误通常不会出现在调试模式下,或者当测试套件单独 运行 时。这让我相信问题可能与时间相关,或者原因是不一致,在某些情况下会在导致任何问题之前自动解决。

public void deleteAllPersons(){
        for(Person person: getAllPersons()){
            long id = person.getId();
            deletePerson(id);
        }

deletePerson(id) 方法是

public void deletePerson(long id) {
      Person person = getPerson(id);             
            List<OrderBill> ordersToBeUnlinked = new ArrayList<>();
            ordersToBeUnlinked.addAll(person.getOrders());

            for (OrderBill order : ordersToBeUnlinked) {                
                person.removeOrder(order);
                System.out.println(order + "deleted");
            }         
        try{    
            manager.getTransaction().begin();
            manager.merge(person);
        } catch (Exception e1){
            throw new DbException("Error merging person" + person + "\n" + e1.getMessage(), e1);
        }
        try {
            manager.remove(person);
            manager.flush();            
            manager.getTransaction().commit();
        } catch (Exception e){
            manager.getTransaction().rollback();
            throw new DbException("error deleting " + person + "\n" + e.getMessage(), e);
        }    
    }

合并正常,但删除时抛出异常。 在异常消息中,我可以看到 Person 的列表中没有更多订单

如果我在 Person 或 OrderBill 中出错 class, 这是这两个 classes 中最重要的部分:

@Entity(name="Person")
public class Person {
    @Id
    @Column(name="PERSON_ID")
    @GeneratedValue
    private long id;

    @ManyToMany(mappedBy="authors", fetch=FetchType.LAZY, cascade={CascadeType.MERGE})
    private Set<OrderBill> orders;
 @Column(name="name")
    private String name = "undefined";
    public Person(String name){
        setName(name);
        payments = new HashSet<Payment>();
        orders = new HashSet<OrderBill>();
    }
    public void removeOrder(OrderBill order){
        orders.remove(order);
       if(order.getAuthors().contains(this)){
            order.removeAuthor(this);
        }
    }

订单单:

@Entity(name="OrderBill")
@NamedQueries({
    @NamedQuery(name="Order.getAll", query="select o from OrderBill o"),
    @NamedQuery(name="Order.findOrders", query="select o from OrderBill o join o.orderWeek as w where (w.weekNr = :w and w.yearNr = :y)")
}) 
public class OrderBill implements Transaction{
    @Id
    @Basic(optional = false)                             
    @NotNull
    @Column(name="ORDER_ID")
    @GeneratedValue
    private long id;

    @ManyToMany
    @JoinTable(
            name="ORDER_PERSON",
            joinColumns=@JoinColumn(name="ORDER_ID"),
            inverseJoinColumns=@JoinColumn(name="PERSON_ID")
    )
    private Set<Person> authors;

        public OrderBill(double totalCost, int weekNr, int yearNr){
            setOrderWeek(new OrderWeek(weekNr, yearNr));
            setTotalCost(totalCost);
            authors = new HashSet<>();
        }

    public void removeAuthor(Person author){
        authors.remove(author);
        if(author.getOrders().contains(this))
            author.removeOrder(this);
    }

如果对某人有帮助,我会在下面写下我的发现:

Chris 的评论被证明是正确的,但我花了很长时间才发现我在尝试删除所有引用时出错的地方。

我不知道这是不是唯一的问题,但我发现了一个主要的逻辑缺陷:

  1. 当我想删除这个人时,我首先从订单列表中删除它的所有引用,然后合并这个人以更新数据库。

但订单列表被标记为 "mappedBy",因此其更改不会反映在数据库中

  1. 我亲自通过 "orders" 列表更新了每个订单,所以我希望级联合并能够处理它

但是通过更新人员,订单列表被清空。因此,当人员合并时,不会留下对更新订单的引用(因此级联合并不会影响他们)

我通过在 deletePerson 的 for 循环中添加 merge(order) 解决了这个问题

for (OrderBill order : ordersToBeUnlinked) {                
    person.removeOrder(order);
    manager.merge(order);
    }

注意:当然,由于合并,我不得不将这个 for 循环放在事务中。