【问题标题】:OneToMany unidirectional mapping in Hibernate. Issue setting foreign key in referenced entityHibernate 中的 OneToMany 单向映射。问题在引用实体中设置外键
【发布时间】:2015-01-11 08:32:45
【问题描述】:

假设有两个实体:ParentChild@OneToManyParent 映射到 Child .

class Parent {
    @Column(name="id")
    private Long id;

    @OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id", referencedColumnName = "id")
    private List<Child> children;
}

class Child {
    @Column(name="id")
    private Long id;

    @Column(name="parent_id")
    private Long parentId;
}

如您所见,在我的例子中,Child 表存储了 Parent 主键的外键。但我不希望它作为我的子实体中的双向映射。

现在出现的问题是,我无法在 Child 实例中设置 parent_id

我创建了这样的实例:

Parent parent = new Parent();
parent.setChildren(Lists.newArrayList(new Child(), new Child()));
parentDomainService.save(parent);

假设在 Parent 端有级联。这种方法首先保存 Parent,然后保存 Child 实例。然后它在子实例上运行更新查询以更新parent_id,正如我从 Hibernate show_sql 日志中看到的那样。但令人惊讶的是,更新查询后,我看到一些childparent_idnull。这让我很惊讶。

所以,我去手动处理那个东西,并删除了级联。然后我像这样保存了实体:

Parent parent = new Parent();
parent.setChildren(Lists.newArrayList(new Child(), new Child()));
parent = parentDomainService.save(parent);

for (Child child: parent.getChildren()) {
    child.setParentId(parent.getId());
} 

childDomainService.save(parent.getChildren());

除了以下例外,这个反弹了我:

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.somepkg.Child

除了那个例外,我已经看到了很多关于 SO 的问题,而且我知道那里有很多问题,但几乎所有问题都在处理双向映射或使用 JoinTable 的单向映射。它们都不适合我的情况。

这个有灯吗?我别无选择。

P.S.:我正在处理的实际场景需要保存大量数据。例如:50000 条父记录和 250000 条子记录。这就是为什么我不想要双向映射。因为保存 Child 会在后端创建一个带有连接表的查询。

我最感兴趣的是解决方案,其中我不必在Child 表上触发两次查询。正如我当前的应用程序中发生的那样,这会影响性能。

【问题讨论】:

    标签: java mysql spring hibernate


    【解决方案1】:

    当您删除级联时,父级不会保留引用的子元素并在

    parent = parentDomainService.save(parent);
    

    父级引用“未保存的瞬态”子实例,因此引发异常。如果您先保存父级,然后添加子级:

    Parent parent = new Parent();
    parent = parentDomainService.save(parent);
    parent.setChildren(Lists.newArrayList(new Child(), new Child()));
    
    for (Child child: parent.getChildren()) {
        child.setParentId(parent.getId());
    } 
    
    childDomainService.save(parent.getChildren());
    

    那么就不会抛出异常了。

    【讨论】:

      猜你喜欢
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      • 2018-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多