Enterprise Java Development@TOPIC@
Uni-directionalOnly 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-directionalBoth 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_INVENTORYFigure 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_BORROWERFigure 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_SUSPECTFigure 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