简体   繁体   中英

@ManyToOne in self-referencing entity with composite key - @IdClass, java, hibernate

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM