带有复合键的自引用实体中的 @ManyToOne - @IdClass、java、hibernate

时间:2021-05-08 17:41:18

标签: java hibernate jpa many-to-one composite-key

花了 3 天时间寻找解决方案,最后我来到这里寻求社区智慧。

我有如下自引用实体:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@IdClass(CompositeUserId.class)
@Table(name = "user", schema = "dbo")
public class User implements Serializable {
    @Id
    @Column(name = "id")
    private Integer id;

    @Id
    @Column(name = "first_name")
    private String firstName;

    @Id
    @Column(name = "last_name")
    private String lastName;

    @ManyToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
    @JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
    @JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
    private User parent;

    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
    private Set<User> children;

我的 CompositeUserId.class:

@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class UserCompositeId implements Serializable {
    private Integer id;
    private String firstName;
    private String lastName;

当我尝试从 user 表中检索所有数据时,出现错误: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ...User with id UserCompositeId@19e66569; nested exception is javax.persistence.EntityNotFoundException:

我想 @JoinColumn 块中可能存在某种错误。

这是导致错误的sql查询:

SELECT * 
FROM dbo.user ur1 LEFT OUTER JOIN dbo.user ur2 ON ur1.first_name=ur2.first_name AND ur1.parent_id=ur2.id AND ur1.last_name=ur2.last_name 
WHERE ur1.first_name='First Name' AND ur1.id=130 AND ur1.last_name='Last Name'

我通过手动运行确保请求不会返回数据库中的任何内容,但发现如果我将 id 更改为 parent_id 它将返回数据,所以再次,可能是一些错误在 @JoinColumn

2 个答案:

答案 0 :(得分:0)

您需要使用 @NotFound(action=NotFoundAction.IGNORE)。如果没有记录,则为它分配 null。

    @ManyToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
    @JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
    @JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
    @NotFound(action=NotFoundAction.IGNORE)
    private User parent;

答案 1 :(得分:0)

外键列需要与主键列不同的名称,例如像这样(注意 name 中的“父”前缀):

@JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = true)
@JoinColumn(name = "parent_firstname", referencedColumnName = "firstname", updatable = false, insertable = true)
@JoinColumn(name = "parent_lastname", referencedColumnName = "lastname", updatable = false, insertable = true)
@ManyToOne(fetch = FetchType.EAGER)
private UserEntity parent;

此外,insertable 的值应为 true,否则在插入期间不会保留任何关联。

请注意,需要先保存父级,然后才能关联子级。由于 parent fk 不可更新,因此必须在保存之前在新创建的子项上设置。

请随意查看包含完整示例代码的 this project
实体: https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/main/java/org/demo/UserEntity.java
集成测试: https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/test/java/org/demo/DemoApplicationTests.java


同步双向关联也是一种很好的做法: https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/

样本实体可以例如使用一些这样的方法:

public void addChild(UserEntity child) {
    child.parent = this; // sync owning side of the association
    this.children.add(child);
}

public void setParent(UserEntity parent) {
    parent.addChild(this);
}