Hibernate删除非孤儿

时间:2016-06-08 14:09:50

标签: java hibernate spring-data

我正在使用Spring Data和Hibernate,CascadeType.ALLorphanRemoval = true

问题是,当将子实体从parentX移动到parentY时,如果parentY在parentX之前持久化,则Hibernate会完全从数据库中删除子实体。之后,孩子仍然存在于内存中的parentY中。如果删除它并且保存了parentY,则会抛出EntityNotFoundException

我有一个SSCE证明了这一点,并且可以在必要时发布,但这似乎是一个简单的问题。

家长实体:

@Entity
public class TestParent implements Serializable {

    private static final long serialVersionUID = 3572015072906463953L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestParent_GENERATOR")
    @SequenceGenerator(name = "TestParent_GENERATOR", initialValue = 1, sequenceName = "TestParent_SEQUENCE", allocationSize = 1)
    private long id;
    private String name;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    @JoinColumn(name = "TestParent_Id")
    private Set<TestChild> testChildren = new HashSet<>();

    @SuppressWarnings("unused")
    private TestParent() {
    }

    public TestParent(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void addChild(TestChild child) {
        this.testChildren.add(child);
    }

    public void removeChild(TestChild child) {
        this.testChildren.remove(child);
    }

    public TestChild findChild(String childsName) {
        for (TestChild testChild : this.testChildren) {
            if (testChild.getName().equals(childsName)) {
                return testChild;
            }
        }
        return null;
    }
}

儿童实体:

@Entity
public class TestChild implements Serializable {

    private static final long serialVersionUID = -1594688339088954284L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TestChild_GENERATOR")
    @SequenceGenerator(name = "TestChild_GENERATOR", initialValue = 1, sequenceName = "TestChild_SEQUENCE", allocationSize = 1)
    private long id;

    private String name;

    @SuppressWarnings("unused")
    private TestChild() {
    }

    public TestChild(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

1 个答案:

答案 0 :(得分:2)

从Hibernate的角度来看,实体映射是不完整的,这就是为什么你可能会得到意想不到的结果。最大的罪魁祸首是orphanRemoval = true,它在不使用mappedBy = ...的情况下使用。虽然JPA规范不需要用mappedBy = ...指定orphanRemoval = true,但是Hibernate无法确定一对多关联的many侧的实体是否真的孤立{if} { <1}}未指定。

以下映射将纠正行为:

mappedBy = ...

请注意,class TestParent { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "testParent") private Set<TestChild> testChildren = new HashSet<TestChild>(); } class TestChild { @JoinColumn(name = "TestParent_Id") @ManyToOne private TestParent testParent; } 需要移至@JoinColumn(name = "TestParent_Id")方。

您还需要非常小心父母的变化。如果孩子留在前一个父母的@ManyToOne集合中,则更改将不会生效。

我创建了一个sample project来演示正常工作的JPA配置。该项目包含一个模拟以下情况的单元测试:

  1. 创建children实例Child
  2. 创建c实例Parent
  3. a实例Child已添加/分配到c实例Parent
  4. a已保存。这级联到a,也会被保存。
  5. 创建了另一个c实例Parent
  6. b实例Child已添加/分配到c实例Parent
  7. b已保存。这级联到b,也会被保存。
  8. 在这种情况下,我们希望执行以下SQL查询:

    c

    如果以INSERT INTO parent (...) VALUES (...); INSERT INTO child (...) VALUES (...); INSERT INTO parent (...) VALUES (...); UPDATE child SET ...; 运行单元测试,您将看到正在按预期执行的SQL查询。