Issue
I am struggling with the following problem that I've been trying to solve. After checking solutions on StackOverflow and articles on Baeldung I still get different JPA errors when trying to map the following ONE-TO-ONE relationship between 2 Oracle SQL tables with composite PK in a SpringBoot application:
MASTER
ID | VERSION |
---|---|
1 | 2022.1 |
Constraint:
PK_MASTER PRIMARY KEY(ID, VERSION)
MASTER_DETAILS
MASTER_ID | VERSION | DETAILS |
---|---|---|
1 | 2022.1 | details |
Constraint:
PK_MASTER_DETAILS PRIMARY KEY(MASTER_ID, VERSION)
FK_MASTER_DETAILS FOREIGN KEY(MASTER_ID, VERSION) REFERENCES MASTER(ID, VERSION)
After some failures in trying to map it using the @OneToOne JPA annotation with both classes having @EmbeddedId set on the composite PK, I also installed JPA Buddy to check how it will be generated and that resulted in the following 4 classes:
Master.java
@Getter
@Setter
@Entity
@Table(name = "master")
public class Master {
@EmbeddedId
private MasterId id;
@OneToOne(mappedBy = "master")
private MasterDetails masterDetails;
}
MasterId.java
@Getter
@Setter
@Embeddable
public class MasterId implements Serializable {
private static final long serialVersionUID = 8254837075462858051L;
@Column(name = "id", nullable = false)
private BigDecimal id;
@Lob
@Column(name = "version", nullable = false)
private String version;
}
MasterDetails.java
@Getter
@Setter
@Entity
@Table(name = "master_details")
public class MasterDetails {
@EmbeddedId
private MasterDetailsId id;
@MapsId
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumns({
@JoinColumn(name = "master_id", referencedColumnName = "id", nullable = false),
@JoinColumn(name = "version", referencedColumnName = "version", nullable = false)
})
private Master master;
@Lob
@Column(name = "details", nullable = false)
private String details;
}
MasterDetailsId.java
@Getter
@Setter
@Embeddable
public class MasterDetailsId implements Serializable {
private static final long serialVersionUID = -8375336118866998644L;
@Column(name = "master_id", nullable = false)
private BigDecimal masterId;
@Lob
@Column(name = "version", nullable = false)
private String version;
}
When running the SpringBoot application with this JPA structure the run time error received is:
org.hibernate.PropertyNotFoundException: Could not locate field [id] on class [org.project.packages.MasterDetails]
After removing the @MapsId that cause this error the application starts but when trying to insert data in the tables I get the following error:
org.hibernate.id.IdentifierGenerationException: null id generated for:class org.project.packages.MasterDetails
Checking in the H2 test database I noticed that the FK on the Master_Details table was not present, but only the PK was set.
I would appreciate any help in pointing out how this problem can be solved: other annotations required (Cascade/FetchType) or in case there are any changes to be made to the database level (I also tried adding a separate identifier column in the Master_Details table defined as PK and only keep the FK to the Master table). Thanks in advance!
Solution
After many tries, I figured out to solve the issue.
I had to use a common key between the two entities and also FetchType.LAZY.
MasterDetails.class
public class MasterDetails {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="ID", column=@Column(name="MASTER_ID"))
})
private MasterId id;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumns({
@JoinColumn(name = "master_id", referencedColumnName = "id", nullable = false),
@JoinColumn(name = "version", referencedColumnName = "version", nullable = false)
})
private Master master;
@Lob
@Column(name = "guidance", nullable = false)
private String guidance;
}
Master.class
public class MasterSheet {
@EmbeddedId
private MasterId id;
@OneToOne(mappedBy = "master", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private MasterDetails masterDetails;
}
Answered By - Fabian
Answer Checked By - David Marino (JavaFixing Volunteer)