Hibernate:@OneToMany:从“Many”端删除实体会导致EntityNotFoundException

时间:2017-10-26 20:08:03

标签: java hibernate jpa

我有以下实体:

@Entity
public static class Parent {
    @Id
    @GeneratedValue
    private Long id;

    String st;

    @OneToMany(mappedBy = "parent")
    Set<Child> children = new HashSet<>();
    // get,set 
}

@Entity
public static class Child {


    @Id
    @GeneratedValue
    private Long id;

    String st;

    @ManyToOne()
    private Parent parent;
    //get,set
  }

请注意,@ OneToMany端没有Cascade。

我想要以下内容:

  1. 我有一个父级,其中一个子级处于独立状态。
  2. 现在我想删除某些条件的孩子,所以我正在访问所有孩子,找到必要的并直接通过em.remove(孩子)将其删除。 +我从Parent的集合中删除它。
  3. 之后我想更改Parent的某些属性并保存它。 而且我得到了EntityNotFound异常。
  4. 我执行了一些调试,发现子集合是PersistentSet,它记住了storedSnapshot中的状态。因此,当我将Parent合并到上下文时--Hibernate会对存储的快照执行某些操作并尝试从DB加载child。当然,没有这样的实体,抛出异常。

    所以,我可以做几件事:

    1. 使用@NotFound(action = NotFoundAction.IGNORE)
    2. 进行地图集合
    3. 从子集合中删除 - 转换为PersistentSet并清除它。
    4. 但它似乎是一个黑客。

      所以, 1.我做错了什么?看来,直接删除子实体是正确的 2.有更优雅的方式来处理这个问题吗?

      可重复的例子:

      @Autowired
      PrentCrud parentDao;
      
      @Autowired
      ChiildCrud childDao;
      @PostConstruct
      public void doSomething() {
      
          LogManager.getLogger("org.hibernate.SQL").setLevel(Level.DEBUG);
      
      
          Parent p = new Parent();
          p.setSt("1");
          Child e = new Child();
          e.setParent(p);
          e.setSt("c");
          p.getChildren().add(e);
          Parent save = parentDao.save(p);
          e.setParent(save);
          childDao.save(e);
          Parent next = parentDao.findAll().iterator().next();
          next.setSt("2");
          next.getChildren().size();
          childDao.deleteAll();
          next.getChildren().clear();
          if (next.getChildren() instanceof PersistentSet) { // this is hack, not working without
              ((Map)((PersistentSet) next.getChildren()).getStoredSnapshot()).clear();
          }
          parentDao.save(next); // exception is thrwn here without hack
          System.out.println("Success");
      
      }
      

2 个答案:

答案 0 :(得分:0)

您是否尝试过将抓取类型更改为急切?关系的默认值      OneToMany: LAZY ManyToOne: EAGER ManyToMany: LAZY OneToOne: EAGER

可能因为获取方法而被缓存

答案 1 :(得分:0)

您可以使用next.setChildren(new HashSet<>());代替next.getChildren().clear();来摆脱getStoredSnapshot()).clear()

但使用cascade和orphanRemoval会更优雅。

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Child> children = new HashSet<>();

public void doSomething() {
    ...
    next.setSt("2");
    next.setChildren(new HashSet<>());
    parentDao.save(next);
    System.out.println("Success");
}