Enterprise Java Development@TOPIC@
Special case of one-to-one mapping
Uses primary key as the foreign key
No additional column for foreign key
Primary keys must match
Figure 60.2. Primary Key Join Example Database Schema
create table ORMREL_BORROWER ( +----- BORROWER_ID bigint not null, | endDate date, | startDate date, | primary key (BORROWER_ID) | ) | create table ORMREL_PERSON ( +----> PERSON_ID bigint generated by default as identity, firstName varchar(255), lastName varchar(255), phone varchar(255), PERSON_PHOTO bigint, primary key (PERSON_ID) ) alter table ORMREL_BORROWER add constraint FKA0973E32F113D9BA foreign key (BORROWER_ID) references ORMREL_PERSON
Figure 60.3. Primary Key Join Example Java Mapping
@Entity @Table(name="ORMREL_BORROWER")
public class Borrower {
@Id @Column(name="BORROWER_ID")
+----- private long id;
|
| @OneToOne(fetch=FetchType.LAZY, optional=false,
| cascade={CascadeType.PERSIST,
| CascadeType.REFRESH,
| CascadeType.MERGE})
| @PrimaryKeyJoinColumn //the two tables will be joined by PKs
+----- private Person identity;
|
| @Entity
| @Table(name="ORMREL_PERSON")
| public class Person {
| @Id @GeneratedValue @Column(name="PERSON_ID")
+----> private long id;
public Borrower(Person identity) {
this.id = identity.getId();
this.identity = identity;
}
In this case, the primary key is used as the foreign key and must be set. To use the foreign key as the primary key -- use @MapsId
//@PrimaryKeyJoinColumn //the two tables will be joined by PKs @MapsId private Person identity; public Borrower(Person identity) { this.identity = identity; }
Figure 60.4. Primary Key Join Example Usage
//create the person we'll use in the relationship
Person person = new Person();
...
//create the Borrower, who requires a Person for its identity
Borrower borrower = new Borrower(person);
borrower.setStartDate(new Date());
//persist the borrower, creating the relationship to person
em.persist(borrower);
logger.info("created borrower: {}", borrower);
assertEquals(person.getId(), borrower.getId()); //ctor copies PK
-Person@61f377d1, ctor()
-
call next value for hibernate_sequence
-created person:Person@61f377d1, id=1, name=jerome doe, phone=410-555-1212, photo=null
-Borrower@27960b1e, ctor():
-created borrower:Borrower,
id=1, startDate=Sat Aug 18 12:00:37 EDT 2018, endDate=null,
identity=Person@61f377d1, id=1, name=jerome doe, phone=410-555-1212, photo=null, applicant=null, checkouts={}
The identity is determined for Person from the database
The identity of Borrower is obtained from Person at some point prior to the next flush cycle
The identity of Person and Borrower will always be the same in a PrimaryKeyJoin
Since Borrower requires a primary key and the column is also a foreign key to Person -- Borrower must always have a Person
Signals primary key column used as foreign key -- no separate foreign key column
name (default=primary key column of this entity)
Names column in this entity's table this property maps to when using composite keys
referencedColumnName (default=primary key column of joined entity)
Names column in joined entity table this property maps to when using composite keys
columnDefinition
Custom DDL definition for column when generating database schema
Child object on many side, using a composite primary key may reference parent with a property from that key
Primary keys cannot be changed
If primary key used -- database manipulation for foreign keys reasons must be turned off
Primary key used to represent foreign key
Primary key must be known -- cannot be derived from generated value
Figure 60.5. Composite @IdClass Property Reused for Foreign Key
@Entity @Table(name="ORMREL_ROOM")
@IdClass(RoomPK.class)
@AttributeOverrides({
@AttributeOverride(name = "houseId", column=@Column(name="HOUSE_ID")),
@AttributeOverride(name = "roomId", column=@Column(name="ROOM_ID"))
})
public class Room {
@Id
private int houseId;
@Id
private int roomId;
@ManyToOne(fetch=FetchType.LAZY, optional=false)
//assign join column to primary key value and turn off inserts/updates here
@JoinColumn(name="HOUSE_ID", insertable=false, updatable=false)
private House house;
public Room() {}
public Room(House house, int roomId) {
this.houseId=house.getId();
this.house=house;
this.roomId=roomId;
}
Setting primary key prior to persisting
Foreign key used twice -- once for relation and once for primary key
Figure 60.6. Referenced Parent Object -- Source of Foreign Key/Primary Key Value
@Entity @Table(name="ORMREL_HOUSE")
public class House {
@Id @GeneratedValue
private int id;
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.REMOVE},
fetch=FetchType.LAZY, mappedBy="house")
private Collection<Room> rooms=new ArrayList<Room>();
Figure 60.7. Composite @IdClass
public class RoomPK implements Serializable {
@Column(name="PK_HOUSE_ID") //overridden
private int houseId;
@Column(name="PK_ROOM_ID") //overridden
private int roomId;
Figure 60.8. Database Schema
create table ORMREL_HOUSE ( id integer generated by default as identity, primary key (id) ) create table ORMREL_ROOM ( HOUSE_ID integer not null, ROOM_ID integer not null, primary key (HOUSE_ID, ROOM_ID) ) alter table ORMREL_ROOM add constraint FKD9EEA1ABC0069D7C foreign key (HOUSE_ID) references ORMREL_HOUSE
HOUSE_ID is both primary and foreign key for ROOM
Figure 60.9. Example Use
House house = new House();
em.persist(house); //generate a PK for parent
house.getRooms().add(new Room(house,0));
house.getRooms().add(new Room(house,1));
house.getRooms().add(new Room(house,2));
em.persist(house); //cascade persists to children
//get a new copy of house
em.flush(); em.clear();
House house2 = em.find(House.class, house.getId());
Figure 60.10. Example Output
Hibernate: insert into ORMREL_HOUSE (id) values (null) Hibernate: insert into ORMREL_ROOM (HOUSE_ID, ROOM_ID) values (?, ?) ... Hibernate: select house0_.id as id1_18_0_ from ORMREL_HOUSE house0_ where house0_.id=? Hibernate: select rooms0_.HOUSE_ID as HOUSE1_18_1_, rooms0_.HOUSE_ID as HOUSE1_30_1_, rooms0_.ROOM_ID as ROOM2_30_1_, rooms0_.HOUSE_ID as HOUSE1_30_0_, rooms0_.ROOM_ID as ROOM2_30_0_ from ORMREL_ROOM rooms0_ where rooms0_.HOUSE_ID=?
Primary key used to represent foreign key
Primary key must be known -- cannot be derived from generated value
Figure 60.11. Composite @EmbeddedId Property Reused for Foreign Key
@Entity @Table(name="ORMREL_DOOR")
public class Door {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="houseId", column=@Column(name="HOUSE_ID")),
@AttributeOverride(name="doorId", column=@Column(name="DOOR_ID"))
})
private DoorPK pk;
@ManyToOne(fetch=FetchType.LAZY, optional=false)
//assign join column to primary key value and turn off inserts/updates here
@JoinColumn(name="HOUSE_ID", insertable=false, updatable=false)
private House house;
public Door() {}
public Door(House house, int doorId) {
pk=new DoorPK(house.getId(), doorId);
this.house=house;
}
Figure 60.12. Composite @Embeddable Class
@Embeddable
public class DoorPK implements Serializable {
@Column(name="PK_HOUSE_ID") //overridden
private int houseId;
@Column(name="PK_DOOR_ID") //overridden
private int doorId;
Figure 60.13. Database Schema
create table ORMREL_DOOR ( DOOR_ID integer not null, HOUSE_ID integer not null, primary key (DOOR_ID, HOUSE_ID) ) alter table ORMREL_DOOR add constraint FKD9E8447EC0069D7C foreign key (HOUSE_ID) references ORMREL_HOUSE
Figure 60.14. Example Use
House house = new House();
em.persist(house); //generate a PK for parent
house.getDoors().add(new Door(house,0));
house.getDoors().add(new Door(house,1));
house.getDoors().add(new Door(house,2));
em.persist(house); //cascade persists to children
//get a new copy of house
em.flush(); em.clear();
House house2 = em.find(House.class, house.getId());
Figure 60.15. Example Output
Hibernate: insert into ORMREL_HOUSE (id) values (null) Hibernate: insert into ORMREL_DOOR (DOOR_ID, HOUSE_ID) values (?, ?) ... Hibernate: select house0_.id as id1_18_0_ from ORMREL_HOUSE house0_ where house0_.id=? Hibernate: select doors0_.HOUSE_ID as HOUSE2_18_1_, doors0_.DOOR_ID as DOOR1_17_1_, doors0_.HOUSE_ID as HOUSE2_17_1_, doors0_.DOOR_ID as DOOR1_17_0_, doors0_.HOUSE_ID as HOUSE2_17_0_ from ORMREL_DOOR doors0_ where doors0_.HOUSE_ID=?
Primary key value is derived from foreign key value
Value can be dynamically generated
Figure 60.16. Composite @IdClass Property Derived from Foreign Key
@Entity @Table(name="ORMREL_RESIDENT")
@IdClass(ResidentPK.class)
@AttributeOverrides({
@AttributeOverride(name = "residentId", column=@Column(name="RESIDENT_ID"))
})
public class Resident {
@Id
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@JoinColumn(name="HOUSE_ID", nullable=false)
private House house;
@Id
private int residentId;
public Resident() {}
public Resident(House house, int residentId) {
this.house=house;
this.residentId=residentId;
}
No longer modeling separate primary key - derived from foreign key
Figure 60.17. Composite @IdClass Class
public class ResidentPK implements Serializable {
private int house;
private int residentId;
Figure 60.18. Database Schema
create table ORMREL_RESIDENT ( HOUSE_ID integer not null, RESIDENT_ID integer not null, primary key (HOUSE_ID, RESIDENT_ID) ) alter table ORMREL_RESIDENT add constraint FKEDC7E20C0069D7C foreign key (HOUSE_ID) references ORMREL_HOUSE
Figure 60.19. Example Use
House house = new House();
em.persist(house); //generate a PK for parent
house.getResidents().add(new Resident(house,0));
house.getResidents().add(new Resident(house,1));
house.getResidents().add(new Resident(house,2));
em.persist(house); //cascade persists to children
//get a new copy of house
em.flush(); em.clear();
House house2 = em.find(House.class, house.getId());
Figure 60.20. Example Output
Hibernate: insert into ORMREL_HOUSE (id) values (null) Hibernate: insert into ORMREL_RESIDENT (HOUSE_ID, RESIDENT_ID) values (?, ?) ... Hibernate: select house0_.id as id1_18_0_ from ORMREL_HOUSE house0_ where house0_.id=? Hibernate: select residents0_.HOUSE_ID as HOUSE1_18_1_, residents0_.HOUSE_ID as HOUSE1_29_1_, residents0_.RESIDENT_ID as RESIDENT2_29_1_, residents0_.HOUSE_ID as HOUSE1_29_0_, residents0_.RESIDENT_ID as RESIDENT2_29_0_ from ORMREL_RESIDENT residents0_ where residents0_.HOUSE_ID=?
Figure 60.21. Composite @EmbeddedId Property Derived from Foreign Key
@Entity @Table(name="ORMREL_MORTGAGE")
public class Mortgage {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="mortgageId", column=@Column(name="MORTGAGE_ID"))
})
private MortgagePK pk;
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@JoinColumn(name="HOUSE_ID", nullable=false)
@MapsId("houseId")
private House house;
public Mortgage() {}
public Mortgage(House house, int mortgageId) {
pk=new MortgagePK(house.getId(), mortgageId);
this.house=house;
}
Figure 60.22. Composite @Embeddable Class
@Embeddable
public class MortgagePK implements Serializable {
@Column(name="PK_HOUSE_ID") //overridden
private int houseId;
@Column(name="PK_MORTGAGE_ID") //overridden
private int mortgageId;
Figure 60.23. Database Schema
create table ORMREL_MORTGAGE ( HOUSE_ID integer not null, MORTGAGE_ID integer not null, primary key (HOUSE_ID, MORTGAGE_ID) ) alter table ORMREL_MORTGAGE add constraint FK175C656CC0069D7C foreign key (HOUSE_ID) references ORMREL_HOUSE
Figure 60.24. Example Use
House house = new House();
em.persist(house); //generate a PK for parent
house.getMortgages().add(new Mortgage(house,0));
house.getMortgages().add(new Mortgage(house,1));
house.getMortgages().add(new Mortgage(house,2));
em.persist(house); //cascade persists to children
//get a new copy of house
em.flush(); em.clear();
House house2 = em.find(House.class, house.getId());
Figure 60.25. Example Output
Hibernate: insert into ORMREL_HOUSE (id) values (null) Hibernate: insert into ORMREL_MORTGAGE (HOUSE_ID, MORTGAGE_ID) values (?, ?) ... Hibernate: select house0_.id as id1_18_0_ from ORMREL_HOUSE house0_ where house0_.id=? Hibernate: select mortgages0_.HOUSE_ID as HOUSE1_18_1_, mortgages0_.HOUSE_ID as HOUSE1_26_1_, mortgages0_.MORTGAGE_ID as MORTGAGE2_26_1_, mortgages0_.HOUSE_ID as HOUSE1_26_0_, mortgages0_.MORTGAGE_ID as MORTGAGE2_26_0_ from ORMREL_MORTGAGE mortgages0_ where mortgages0_.HOUSE_ID=?
Separate table created to hold foreign keys
Can be used for all relationship enumatations and directions
Figure 60.27. Join/Link Table Database Table Schema
create table ORMREL_INVENTORY (
/----> id bigint generated by default as identity,
| name varchar(255),
| primary key (id)
| )
| create table ORMREL_INVENTORY_MEDIA (
`----- ORMREL_INVENTORY_id bigint not null,
/----- media_MEDIA_ID bigint not null
| )
| create table ORMREL_MEDIA (
`----> MEDIA_ID bigint generated by default as identity,
title varchar(255),
primary key (MEDIA_ID)
)
Join table name either derived from associated tables or explicitly named
Join table columns either derived from referenced table column or explicitly named
Figure 60.28. Join/Link Table Database Constraint Schema
alter table ORMREL_INVENTORY_MEDIA add constraint UK_F6FA5C31B7DAA951 unique (media_MEDIA_ID) alter table ORMREL_INVENTORY_MEDIA add constraint FKF6FA5C31A70D4E48 foreign key (media_MEDIA_ID) references ORMREL_MEDIA alter table ORMREL_INVENTORY_MEDIA add constraint FKF6FA5C317DD5E49D foreign key (ORMREL_INVENTORY_id) references ORMREL_INVENTORY
Unique constraint enforces the (1)-to-Many aspect of relationship
Figure 60.29. Join/Link Table Database Java Mapping
@Entity @Table(name="ORMREL_INVENTORY")
public class Inventory {
@Id @GeneratedValue
private long id;
private String name;
@OneToMany(cascade={CascadeType.ALL})
@JoinTable(name="ORMREL_INVENTORY_MEDIA")
/----- private Collection<Media> media = new ArrayList<Media>();
|
| @Entity @Table(name="ORMREL_MEDIA")
| public class Media {
| @Id @GeneratedValue @Column(name="MEDIA_ID")
`----> private long id;
name
Database table name for join table
catalog
Database catalog for join table
schema
Database schema for join table
joinColumns
List of @JoinColumn definitions from join table back to owning entity class table
inverseJoinColumns
List of @JoinColumn definitions from join table to inverse entity class table
uniqueConstraints
Uniqueness constraints to be added to join table