Enterprise Java Development@TOPIC@
Uni-directional
Only one side ("owner") knows of the relationship
Uses the @OneToMany annotation
Defines mapping to database
Uses either @JoinColumn or @JoinTable
@JoinTable adds the foreign key to the child table and not to the owning entity class table in this uni-directional case
Bi-directional
Both classes know of the relationship
Many side required to be owning side and maps relation to the database
Uses the @ManyToOne annotation
Uses either @JoinColumn or @JoinTable
Changes here change the database
One side required to be inverse and names the other entity's property
@OneToMany(mappedBy="owning-property")
Changes here do *not* change database
This example uses the @JoinColumn technique of inserting foreign key into child table of inverse entity class
No construct in child/inverse side to map to foreign key
Figure 57.3. One-to-Many Uni-directional Database Schema
create table ORMREL_INVENTORY ( /----> id bigint generated by default as identity, | name varchar(255), | primary key (id) | ) | create table ORMREL_MEDIA ( | MEDIA_ID bigint generated by default as identity, | title varchar(255), `----- INVENTORY_ID bigint, primary key (MEDIA_ID) ) alter table ORMREL_MEDIA add constraint FK639A68F4BCF517CD foreign key (INVENTORY_ID) references ORMREL_INVENTORY
Figure 57.4. One-to-Many Uni-directional Database Java Mapping
@Entity @Table(name="ORMREL_INVENTORY")
public class Inventory {
@Id @GeneratedValue
private long id;
private String name;
@OneToMany(cascade={CascadeType.ALL})
/----- @JoinColumn(name="INVENTORY_ID")
| private Collection<Media> media = new ArrayList<Media>();
|
| @Entity @Table(name="ORMREL_MEDIA")
| public class Media {
| @Id @GeneratedValue @Column(name="MEDIA_ID")
`----> private long id;
Figure 57.5. One-to-Many Uni-directional Database Usage
ejava.examples.orm.rel.annotated.Inventory inventory = new Inventory();
inventory.setName("testLinkCreate");
em.persist(inventory);
for(int i=0; i<5; i++) {
ejava.examples.orm.rel.annotated.Media media = new Media();
em.persist(media);
logger.info("created media: {}", media);
inventory.getMedia().add(media);
}
logger.info("created inventory:{}", inventory);
Relationship formed when inverse side added to owning collection
Figure 57.6. One-to-Many Uni-directional Database Usage
- call next value for hibernate_sequence - call next value for hibernate_sequence -created media:Media@7b222230, id=2, title=null, authors(0)={} - call next value for hibernate_sequence -created media:Media@7df6d663, id=3, title=null, authors(0)={} ... - insert into ORMREL_INVENTORY (name, id) values (?, ?) - insert into ORMREL_MEDIA (title, MEDIA_ID) values (?, ?) - insert into ORMREL_MEDIA (title, MEDIA_ID) values (?, ?) ... -created inventory:Inventory@4ef4f627, id=1, name=testLinkCreate, media(5)={2,3,4,5,6,}
The foreign key is in the inverse entity class table
No construct in inverse class maps to this foreign key column
Identical properties to @OneToOne annotation with the exception of no "optional" property
Figure 57.7. One-to-Many Bi-directional Example Database Schema
create table ORMREL_BORROWER ( +----> BORROWER_ID bigint not null, | endDate date, | startDate date, | primary key (BORROWER_ID) | ) | create table ORMREL_CHECKOUT ( | CHECKOUT_ID bigint generated by default as identity, | outDate date, | returnDate date, `----- CHECKOUT_BID bigint not null, primary key (CHECKOUT_ID) ) alter table ORMREL_CHECKOUT add constraint FK7F287E16C07B41F3 foreign key (CHECKOUT_BID) references ORMREL_BORROWER
Figure 57.8. One-to-Many Bi-directional Example Java Mapping
@Entity @Table(name="ORMREL_CHECKOUT")
public class Checkout {
@Id @GeneratedValue @Column(name="CHECKOUT_ID")
private long id;
+--> @ManyToOne(optional=false)
| @JoinColumn(name="CHECKOUT_BID")
+----- private Borrower borrower;
| |
| | @Entity @Table(name="ORMREL_BORROWER")
| | public class Borrower {
| | private static Log log = LogFactory.getLog(Borrower.class);
| | @Id @Column(name="BORROWER_ID")
`----> private long id;
|
| @OneToMany(mappedBy="borrower", //this relationship is owned by Checkout
| fetch=FetchType.LAZY) //try to limit what we get back
`--- private Collection<Checkout> checkouts = new ArrayList<Checkout>();
Figure 57.9. One-to-Many Bi-directional Example Usage (Create)
//get a borrower
Borrower borrower = em.find(Borrower.class, borrowerId);
assertNotNull(borrower);
assertEquals(0, borrower.getCheckouts().size());
//create 1st checkout
Checkout checkout = new Checkout(new Date());
checkout.setBorrower(borrower); //set owning side of the relation
//wrapper around - borrower.getCheckouts().add(checkout)
borrower.addCheckout(checkout); //set inverse side of relation
em.persist(checkout); //persist owning side of the relation
em.flush();
-getting borrower id=1 - select borrower0_.BORROWER_ID as BORROWER1_15_0_, borrower0_.endDate as endDate2_15_0_, borrower0_.startDate as startDat3_15_0_ from ORMREL_BORROWER borrower0_ where borrower0_.BORROWER_ID=? ... - call next value for hibernate_sequence - insert into ORMREL_CHECKOUT (CHECKOUT_BID, outDate, returnDate, CHECKOUT_ID) values (?, ?, ?, ?)
//get a borrower
//create a couple more
for(int i=0; i<5; i++) {
Checkout co = new Checkout(new Date());
co.setBorrower(borrower); //set owning side of the relation
borrower.addCheckout(co); //set inverse side of relation
em.persist(co); //persist owning side of the relation
}
em.flush();
- call next value for hibernate_sequence ... - insert into ORMREL_CHECKOUT (CHECKOUT_BID, outDate, returnDate, CHECKOUT_ID) values (?, ?, ?, ?) ...
Figure 57.10. One-to-Many Bi-directional Example Usage (Verify)
//check the DB
em.flush(); em.clear();
Borrower borrower2 = em.find(Borrower.class, borrower.getId());
assertEquals(6, borrower2.getCheckouts().size());
Maps collection of simple data types to child table
Uses orphanRemoval for child values
Figure 57.11. Element Collection Example Database Schema
create table RELATIONEX_SUSPECT ( +----> id integer generated by default as identity, | name varchar(32), | primary key (id) | ) | create table RELATIONEX_SUSPECT_ALIASES ( `----- SUSPECT_ID integer not null, ALIAS varchar(32), unique (SUSPECT_ID, ALIAS) ) alter table RELATIONEX_SUSPECT_ALIASES add constraint FK3FD160E6DE29C9CF foreign key (SUSPECT_ID) references RELATIONEX_SUSPECT
Figure 57.12. Element Collection Example Database Java Mapping
@Entity
@Table(name="RELATIONEX_SUSPECT")
public class Suspect {
@Id @GeneratedValue
private int id;
@Column(length=32)
private String name;
@ElementCollection
@CollectionTable(
name="RELATIONEX_SUSPECT_ALIASES",
joinColumns=@JoinColumn(name="SUSPECT_ID"),
uniqueConstraints=@UniqueConstraint(columnNames={"SUSPECT_ID", "ALIAS"}))
@Column(name="ALIAS", length=32)
private Set<String> aliases;
Figure 57.13. Element Collection Example Database Usage
Suspect suspect = new Suspect(); suspect.setName("william"); em.persist(suspect); suspect.getAliases().add("bill"); suspect.getAliases().add("billy");
Link Table
Foreign key not placed in child table
Separate table used to realize relationship
Discussed in Join Tables section