在使用 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 的评论被证明是正确的,但我花了很长时间才发现我在尝试删除所有引用时出错的地方。
我不知道这是不是唯一的问题,但我发现了一个主要的逻辑缺陷:
- 当我想删除这个人时,我首先从订单列表中删除它的所有引用,然后合并这个人以更新数据库。
但订单列表被标记为 "mappedBy",因此其更改不会反映在数据库中
- 我亲自通过 "orders" 列表更新了每个订单,所以我希望级联合并能够处理它
但是通过更新人员,订单列表被清空。因此,当人员合并时,不会留下对更新订单的引用(因此级联合并不会影响他们)
我通过在 deletePerson 的 for 循环中添加 merge(order) 解决了这个问题
for (OrderBill order : ordersToBeUnlinked) {
person.removeOrder(order);
manager.merge(order);
}
注意:当然,由于合并,我不得不将这个 for 循环放在事务中。
我随机得到以下异常: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 的评论被证明是正确的,但我花了很长时间才发现我在尝试删除所有引用时出错的地方。
我不知道这是不是唯一的问题,但我发现了一个主要的逻辑缺陷:
- 当我想删除这个人时,我首先从订单列表中删除它的所有引用,然后合并这个人以更新数据库。
但订单列表被标记为 "mappedBy",因此其更改不会反映在数据库中
- 我亲自通过 "orders" 列表更新了每个订单,所以我希望级联合并能够处理它
但是通过更新人员,订单列表被清空。因此,当人员合并时,不会留下对更新订单的引用(因此级联合并不会影响他们)
我通过在 deletePerson 的 for 循环中添加 merge(order) 解决了这个问题
for (OrderBill order : ordersToBeUnlinked) {
person.removeOrder(order);
manager.merge(order);
}
注意:当然,由于合并,我不得不将这个 for 循环放在事务中。