Reputation: 9069
I'm using JPA 2.x
with EclipseLink 2.5.1
with nested primary key class
as the following: -
1. OS: Windows 7 64 Bits
2. JDK: 1.7.0_65 64 Bits
3. Maven: 3.2.2
4. Arquillian: 1.1.5.Final
5. Container: Glassfish 4 embedded
6. Database: Derby 10.10.2.0 embedded
@Embeddable
public class MasterPk {
@Column(
name = "MASTER_ID",
length = 5
)
private String masterId;
//----> Setter/Getter is omitted
}
@Entity
@Table(name = "MASTER_TAB")
public class Master {
@EmbeddedId
private MaskerPk id;
@Column(
name = "MASTER_NAME",
length = 40
)
private String masterName;
@OneToMany(
mappedBy = "master"
)
private List<Detail> details;
//----> Setter/Getter is omitted
}
@Embeddable
public class DetailPk {
//----> THE NESTED IS HERE.
@Embedded
private MasterPk masterId;
@Column(
name = "DETAIL_ID",
length = 5
)
private String detailId;
//----> Setter/Getter is omitted
}
@Entity
@Table(name = "DETAIL_TAB")
public class Detail {
@EmbeddedId
private DetailPk id;
@Column(
name = "DETAIL_NAME",
length = 40
)
private String detailName;
@MapsId("masterId")
@ManyToOne
@JoinColumns({
@JoinColumn(
name = "MASTER_ID",
referencedColumnName = "MASTER_ID",
nullable = false
)
})
private Master master;
//----> Setter/Getter is omitted
}
MasterPk masterPk = new MasterPk();
masterPk.setId("001"); //there is a 001 existed in the db
DetailPk detailPk = new DetailPk();
detailPk.setMasterId(masterPk);
detailPk.setDetailId("001"); //new detail to persist
Detail detail = new Detail();
detail.id(detailPk);
detail.setDetailName("detail-name");
em.persist(detail);
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="unittest" transaction-type="JTA">
<jta-data-source>jdbc/unittest</jta-data-source>
<class>com.test.MasterPk</class>
<class>com.test.Master</class>
<class>com.test.DetiailPk</class>
<class>com.test.Detail</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.ddl-generation"
value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode"
value="both"/>
<property name="eclipselink.application-location"
value="target"/>
<property name="eclipselink.create-ddl-jdbc-file-name"
value="createDDL_ddlGeneration.jdbc"/>
<property name="eclipselink.drop-ddl-jdbc-file-name"
value="dropDDL_ddlGeneration.jdbc"/>
</properties>
</persistence-unit>
</persistence>
When the project starts, all tables are created correctly, especially the primary key
and foreign key
. But when persist
with the coding., there are some trouble about the Detail
as the Detail.masterId.id
is null. The EclipseLink
shows me as
Column 'MASTER_ID' cannot accept a NULL value.
INSERT INTO DETAIL_TAB (DETAILE_NAME, DETAIL_ID, MASTER_ID)
VALUES (?, ?, ?)
bind => [detail-name, 001, null]
//
//----> We may be noticed that the masterId is not null. It is printed as 001.
//
Query: InsertObjectQuery(Detail(detailName=detail-name,
id=DetailPk(detailId=001,
masterId=(MasterPk(masterId=001))
)
)
)
I double check by printing all field
in this object-graph
and can confirm that the masterId
is not null as well. I'm not sure if doing something wrong/misunderstanding. Could you please help to advise how to solve this issue?
@ManyToOne
at the Detail.java
. It is a typo when copying and pasting to this questionTry to add the @Embedded
to the masterId
at the class DetailPk
.
Edit 1 result still faces the same issue.
detail
and persist
I create the MasterPk
directly without finding the Master
.
Further question, Should I need to find the Mater
entity for getting its id
and assigning it to the DetailPk
instead of create it directly via new MasterPk();
?
Upvotes: 1
Views: 867
Reputation: 8219
As MasterPK
is an embeddable it should be annotated with @Embedded
:
@Embeddable
public class DetailPk {
@Embedded
private MasterPk masterId;
...
}
I guess Detail
entity is also missing @ManyToOne
(or it's just intentional?)
@Entity
@Table(name = "DETAIL_TAB")
public class Detail {
...
@MapsId("masterId")
@ManyToOne
@JoinColumns({
@JoinColumn(
name = "MASTER_ID",
referencedColumnName = "MASTER_ID",
nullable = false
)
})
private Master master;
}
Ad Edit 2
The following statement
INSERT INTO DETAIL_TAB (DETAILE_NAME, DETAIL_ID, MASTER_ID)
VALUES (?, ?, ?)
bind => [detail-name, 001, null]
means that the owning side of the master-detail relationship (represented by @ManyToOne
association) is null.
From JPA point of view this looks as follows:
MASTER_ID
column is related to master
field in Detail
entityMaster
entity uses MasterPK
as the primary keyDetail
entity references Master
with master
fieldDetail
entity acts as the owner of the relationship it would be good to specify cascading on it, i.e. @ManyToOne(cascade = CascadeType.PERSIST)
Based on the above, to make thing work we would need to set up the owning side, for example:
Master master = new Master();
master.id(masterPk);
Detail detail = new Detail();
detail.id(detailPk);
detail.setMaster(master); // the owning side of the master-detail relationship
master.setDetail(detail); // mandatory if CascadeType.PERSIST is not defined
em.persist(detail);
This will result in:
INSERT INTO DETAIL_TAB (DETAIL_NAME, DETAIL_ID, MASTER_ID)
VALUES (?, ?, ?)
bind => [detail-name, 001, 001]
Upvotes: 1