Spring Data / hibernate更新子DTO对象和@MapsId

时间:2016-10-23 01:16:54

标签: spring hibernate jpa spring-data

当我尝试更新Parent并且Child是DTO时,我收到了EntityExistException。我该如何解决这个问题?我试过在Parent上调用merge,没有运气。我正在使用Spring数据jpa和hibernate。

@Entity
class Parent {
    @Id
    private int id;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "parent")
    private Child child;

    // getters and setters
}

@Entity
class Child {
    @Id
    private int id;

    private String data;

    @MapsId
    @OneToOne
    private Parent parent;

    // getters and setters
}

在我的休息服务中,我正在做以下事情:

Child child = new Child(data); // data from request
Parent parent = parentRepositoryService.getParent(id);
parent.setChild(child);
parentRepositoryService.update(parent);

parentRepositoryService.class

class ParentRepositoryService {
    public void update(Parent parent) {
        parent.getChild().setParent(parent);  // necessary to prevent Exception
        parentRepository.save(parent);
    }
}

我认为问题是Child对象已经在“parentRepositoryService.getParent(id)”调用的会话中。然后在会话外创建Child对象,导致旧的和新的Child对象之间不匹配。我宁愿不必从请求数据手动更新基于会话的Child对象,并且只想为子实体执行mysql“insert ... on duplicate key update”的等价物。

1 个答案:

答案 0 :(得分:0)

我认为这是发生的事情:

您加载了一个包含子P的父C1。所以你现在有以下对象:

P -> C1
C1 -> P

箭头表示引用。

现在您创建一个新的子C2并将其设置为P的子项,并最终得到以下对象:

P -> C2
C1 -> P
C2 -> P

请注意C1仍引用P。当会话被刷新时,Hibernate使用子句中的引用来保持关系,并且你得到一个例外,因为P被两个孩子引用。

为了修复它,取消将旧子项的引用设置为父项,您甚至可能希望将其删除。这应该发生在Parent类中,就像

一样
parent.getChild().setParent(parent)

应该是Parent.setChild

的一部分