问题描述
花了 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",updatable = false)
@JoinColumn(name = "last_name",referencedColumnName = "last_name",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
块中可能存在某种错误。
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
块
解决方法
您需要使用 @NotFound(action=NotFoundAction.IGNORE)
。如果没有记录,则为它分配 null。
@ManyToOne
@JoinColumn(name = "parent_id",referencedColumnName = "id",insertable = false,updatable = false)
@JoinColumn(name = "first_name",referencedColumnName = "first_name",updatable = false)
@JoinColumn(name = "last_name",referencedColumnName = "last_name",updatable = false)
@NotFound(action=NotFoundAction.IGNORE)
private User parent;
,
外键列需要与主键列不同的名称,例如像这样(注意 name
中的“父”前缀):
@JoinColumn(name = "parent_id",updatable = false,insertable = true)
@JoinColumn(name = "parent_firstname",referencedColumnName = "firstname",insertable = true)
@JoinColumn(name = "parent_lastname",referencedColumnName = "lastname",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);
}