Hibernate级联“一对多”无法正确地将孩子与父母联系起来

时间:2018-07-18 16:17:07

标签: java spring hibernate jhipster mapstruct

我有一个Java API(使用Spring框架),前端使用它来创建新的父对象(json),其中包含一个新的子对象列表,如下所示:

parent = {
    children: [
    {
        childName: 'name1'
    },
    {
        childName: 'name2'
    }]   
}

您会看到那里没有ID,因为我要保存它们,因此数据库应该生成ID。

父级实体在后端中具有以下代码:

@OneToMany(cascade = {CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "parent")
private Set<Child> children = new HashSet<>();

在父级上调用存储库的“保存”操作后,我可以在数据库中找到子级元素(因此级联工作),但是数据库中子级的“ parentId”列为空,因此当我获取父级,其子级列表显示为空。

我可以通过在服务类中执行以下操作来解决此问题:

repository.save(parent);
parent.getChildren().forEach((child) -> child.setParent(parent));

,它之所以有效,是因为(到时候为止)父数据库已经为数据库生成了一个ID,因此子表可以将该ID用于其parentId列。它可以工作,但是感觉应该是标准的一对多关系,并且应该由框架以比这更漂亮的方式自动处理。所以我的问题是:我想念什么?

在级联操作期间保存时,Hibernate是否有某种方式可以自动填充子实体的“ parentId”列?

编辑: 实际上,这可能是由我用来将DTO映射到POJO并返回的MapStruct引起的。尚不确定,但会继续调查(请参阅相关链接:https://github.com/mapstruct/mapstruct/issues/1068

3 个答案:

答案 0 :(得分:0)

以下是有关如何创建实体和添加子代的一些基础知识。如您所见,您将不必遍历子级并在那里设置父级的引用。

public class Parent implements Serializable {
    //bi-directional many-to-one association to Children
    @OneToMany(mappedBy="Parent")
    private List<Children> children;

    public List<Children> getChildren() {
        return children;
    }

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

    public Children addChildren(Children child) {
        getChildren().add(child);
        child.setParent(this);

        return child;
    }

    public Children removeChildren(Children child) {
        getChildren().remove(child);
        child.setParent(null);

        return child;
    }
}

现在创建父对象并调用addChildren以正确设置引用。希望对您有所帮助。

答案 1 :(得分:0)

我知道了,对不起,这确实不是Hibernate的问题。

问题是我使用MapStruct来使用更简单的前端对象。这将对象分为POJO和它们各自的DTO对象。 DTO对象没有对整个其他对象的引用,它们只是复制了一些更相关的部分以简化json。

例如,ChildDTO仅具有

{ 
    name : 'name',
    parentId: '1'
}

,然后在从前端到后端进行调用时使用MapStruct,将DTO转换为真实的Java对象,在数据库(“ ish”)中按ID查找父对象,并将其添加到子对象中。但是默认情况下,它不知道将父引用添加到未设置parentId的子元素中,即使它们作为父元素的一部分而出现((!)。因此,在Java Child类中-父引用为null。

至少有两种解决方案:

MapStruct解决方案:

向MapStruct映射器添加自定义映射代码,以手动设置作为父级一部分发送的所有子元素的父级引用。参见here

Java解决方案:

在MapStruct运行后但保存到数据库之前,将父级引用手动添加到所有子级:

// MapStruct first maps the DTO to a real Parent object, then:
parent.getChildren().forEach((child) -> child.setParent(parent));
parentRepository.save(parent);

答案 2 :(得分:0)

很棒的解决方案-几天来一直遇到相同的问题,我有一个层叠的oneToOne,oneToMany等,设置生成一个相当广泛的xml。我仍然必须在父级上添加级联。

GrandParent finalGrandParent = grandparent;
grandparent.getParent().getChildren().forEach(child -> child.setParent(finalGrandParent.getParent()));