Spent 3 days looking for a solution and finally I came here for community wisdom.
I have self-referencing entity like follows:
@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;
my CompositeUserId.class:
@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class UserCompositeId implements Serializable {
private Integer id;
private String firstName;
private String lastName;
When I try retrieve all data from my user
table I get error: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find...User with id UserCompositeId@19e66569; nested exception is javax.persistence.EntityNotFoundException:
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find...User with id UserCompositeId@19e66569; nested exception is javax.persistence.EntityNotFoundException:
I suppose there might be some kind of mistake in the @JoinColumn
block.
Here is the sql query causing the error:
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'
I ensured that the request does not return anything in the database by running it manually, but found out that if I will change id
to parent_id
it will return data, so again, probably some mistake in @JoinColumn
block
You need you use @NotFound(action=NotFoundAction.IGNORE)
. If there is no records then it will assign null to it.
@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;
The foreign key columns need different names than the primary key columns, eg like this (notice the 'parent' prefix in 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;
Additionally, the insertable
value should be true
, since otherwise no associations are persisted during the insert.
Be aware that a parent needs to be saved before a child can be associated. Since the parent
fk is not updatable, is must be set on a newly created child before it is saved.
Feel free to have a look at this project containing the complete sample code:
Entity: https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/main/java/org/demo/UserEntity.java
Integration-Test: https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/test/java/org/demo/DemoApplicationTests.java
Also it is good practice to synchronize bidirectional associations: https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
The sample entity could eg use some methods like these:
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);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.