OneToMany的{Hibernate级联问题

时间:2017-02-13 04:31:42

标签: java hibernate

我正在尝试理解如何正确地注释类以使级联工作。

这是一个简单的父/子场景。

子:

@Entity
@Table(name = "testChild")
public class testChild implements Serializable {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(name = "uuid", unique = true, nullable = false)
    @JsonProperty
    private String uuid;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="parent_uuid", nullable=false)
    @Cascade(value={CascadeType.ALL})
    @JsonIgnore
    private testParent parent;

    @Column(name = "name")
    @JsonProperty
    private String name;    

    public testChild (testParent parent, String name) {
        this.uuid = UUID.randomUUID().toString();
        this.name = name;
        this.parent = parent;
    }

    public String getUuid() {
        return this.uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    public testParent getParent () {
        return this.parent;
    }

    public void setParent (testParent parent) {
        this.parent = parent;
    }
}

父:

    @Entity
@Table(name = "testParent")
public class testParent implements Serializable {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    @Column(name = "uuid", unique = true, nullable = false)
    @JsonProperty
    private String uuid;        // This is the figure uuid:<number> 

    @Column(name = "name")
    @JsonProperty
    private String name;    

    @Column(name = "children")
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval=true, cascade = javax.persistence.CascadeType.ALL)
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
    @JsonProperty
    private List <testChild> children;    

    public testParent (String name) {
        this.uuid = UUID.randomUUID().toString();
        this.name = name;
        this.children = new ArrayList<>();
    }

    public String getUuid() {
        return this.uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    public List <testChild> getChildren () {
        return this.children;
    }

    public void setChildren (List <testChild> children) {
        this.children = children;
    }

    public testChild addChild(String childname) {
        testChild c = new testChild(this, childname);
        this.children.add(c);
        return c;
    }
}

测试代码:

testParent p = new testParent("Dan");
sid = p.getUuid();

testChild c1 = p.addChild("Dan jr.");
testChild c2 = p.addChild("Dannielle");
session.save(p);

我的问题是它导致对(第一个)子节点的更新而不是插入:

DEBUG org.hibernate.SQL - insert into testParent (name, uuid) values (?, ?)
DEBUG org.hibernate.SQL - update testChild set name=?, parent_uuid=? where uuid=?

当然这会引发错误。 我在父类和子类中为Cascade尝试了很多组合。

唯一可行的是,如果我一直明确地“session.save(object)”。即 创建父 - 保存, 创建child1 - 保存, 创建child2 - 保存, 最后再次保存父母。

我真的很想知道如何告诉Hibernate从上到下简单地保存所有内容。

从这个网站和其他网站尝试了很多不同的建议,但似乎没有任何工作。

2 个答案:

答案 0 :(得分:0)

您必须明确地将setter用于父级和子级。

以下是您需要做的事情:

 testParent p = new testParent("Dan");

 testChild c1 = new testChild("Dan");
 testChild c2 = new testChild("Dannielle");

 c1.setParent(p);
 c2.setParent(p);

 p.getChildren().add(c1);
 p.getChildren().add(c2);
 session.save(p);

你也可能混淆了实体级联。在OneToMany方面,即testParent,您需要CascadeType.ALL或包含CascadeType.PERSIST。您想说的是,无论何时您想要“插入”或“更新”或“删除”父母,也请对孩子们采取相同的行动。

父级的save也应该将保存级联到其引用的子级。

答案 1 :(得分:0)

我有一个部分答案。它解决了这个问题,但我仍然有点困惑。 问题与使用生成的ID有关。我也试图手动设置它们,我认为它打破了父母和孩子之间的映射。

以下是一些有效的代码:

    try {
        testParent p = new testParent("Dan");
        sid = p.getUuid();

        testChild c1 = new testChild("Dan Jr.");
        testChild c2 = new testChild("Danielle");

        c1.setParent(p);
        c2.setParent(p);
        p.getChildren().add(c1);
        p.getChildren().add(c2);

        session.save(p);            
        tx.commit();

    } catch(Exception e) {
        tx.rollback();
        log.error("Exception occured. "+e.getMessage());
        assertTrue(false);
    }

但它只在我做两件事情时才有效:

  1. 从类定义中删除@GeneratedValue@GenericGenerator注释。
  2. 确保我的构造函数手动创建这些id字段。
  3. 如果我不这样做,那么似乎setUuid在持久保存到数据库时不会更新uuid字段。该对象获取新的id,但只保存原始生成的ID。

    我想知道这是否是最好的方法。