在与jpa使用manytomany关系时,如何避免获取ValidationException?

时间:2016-09-09 16:26:29

标签: java jpa eclipselink

我随机获得以下异常: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()”时会抛出异常。当两个实体之间存在必须删除的链接时,会出现错误,并且似乎发生在我的案例中被删除的第二个人身上。

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

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类中犯了错误, 以下是这两个类中最重要的部分:

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

OrderBill:

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

1 个答案:

答案 0 :(得分:0)

如果它可以帮助某人,我会写下我在下面找到的内容:

克里斯的评论结果证明是正确的,但是在我发现我在尝试删除所有引用时出错的时候花了很长时间。

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

  1. 当我想要删除该人时,我首先从订单列表中删除所有引用,然后合并该人以更新数据库。
  2. 但订单列表标记为“mappedBy”,因此其更改不会反映在数据库中

    1. 我已经通过“订单”列表亲自更新了每个订单,所以我希望级联合并能够处理它
    2. 但是通过更新此人,带有订单的列表将被清空。因此,当合并人员时不会留下对更新订单的引用(因此级联合并不会影响他们)

      我通过在deletePerson

      中的for循环中添加merge(order)来解决问题
      for (OrderBill order : ordersToBeUnlinked) {                
          person.removeOrder(order);
          manager.merge(order);
          }
      

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