Title: Java Persistence: Object Relationship Mapping
1Java PersistenceObject RelationshipMapping
2Goals
- Be able to map a relationships between classes to
the database using class annotations - coverage of ORM descriptors can be found in text
or JPA Spec - Be able to navigate relationships between objects
persisted to the database using get/set API calls
instead of SQL
3Objectives
- Relationship Sides (owning and inverse)
- Relationship Types
- OneToOne
- ManyToOne
- OneToMany
- ManyToMany
- Relationship Directions
- uni-directional
- bi-directional
- Collection Types
- FetchType
- CascadeType
4OneToOne
- Uni-directional
- Bi-Directional
5OneToOne
- Uni-directional
- only one class (owner) knows of the
relationship - uses _at_OneToOne annotation
- this class defines the mapping to the database
- may use _at_JoinColumn or _at_JoinTable annotation
- Bi-Directional
- both classes know of the relationship
- use _at_OneToOne annotation
- one class (owner) defines the mapping to the
database - may use _at_JoinColumn or _at_JoinTable annotation
- the other class (inverse side) names the
owner's property that defines the relationship - uses _at_OneToOne(mappedByremote property)
6OneToOne Uni-directional Example DB Schema
- create table ORMREL_PHOTO (PHOTO_ID bigint
generated by default as identity (..., image
longvarbinary, primary key (PHOTO_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_PERSON add constraint
FK14D7C425DCCB1C0D foreign key (PERSON_PHOTO)
references ORMREL_PHOTO -
7OneToOne Uni-directional Example Java Mapping
- _at_Entity _at_Table(name"ORMREL_PHOTO")
- public class Photo
- _at_Id _at_GeneratedValue _at_Column(name"PHOTO_ID")
- private long id
- _at_Lob
- private byte image
- _at_Entity _at_Table(name"ORMREL_PERSON")
- public class Person
- _at_Id _at_GeneratedValue _at_Column(name"PERSON_ID")
- private long id
- private String firstName
- private String lastName
- private String phone
- _at_OneToOne(cascadeCascadeType.ALL,
- fetchFetchType.LAZY)
- _at_JoinColumn(name"PERSON_PHOTO")
- private Photo photo
-
There is no inverse relationship definition for
uni-directional relationships
Owning side uses a JoinColumn to name the
foreign key column for the relationship
8_at_OneToOne Annotation
- public interface OneToOne extends ...
- Class targetEntity() default void.class
- entity class related to
- can usually be determined by property type
- CascadeType cascade() default
- defines which persistence operations to cascade
to related object - FetchType fetch() default EAGER
- hint to provider as to whether related object
necessary - boolean optional() default true
- states whether value of relationship can be null
- String mappedBy() default
- only used in bi-directional relationships on the
inverse side - names remote property in owning class that
defines mapping to DB
9FetchType (hint)
- EAGER
- related object is always instantiated from the
database when this object is instantiated - LAZY
- related object not instantiated from database
when this object is instantiated - proxy is acting as a place-holder
- undefined implementation by spec
- byte code manipulation of entity class required
option of Java EE environment - post-compilation step option for Java SE
environment - dynamic proxy classes
- object is fetched and instantiated when proxy
accessed and object is still attached to the
persistence context - exception thrown (undefined by spec) if proxy
accessed after object is detached from
persistence context
10_at_JoinColumn Annotation
- public interface JoinColumn extends ...
- very similar to _at_Column annotation
- String name() default
- name of database column for foreign key
- String referencedColumnName() default (PK
column of related object) - used to reference a non-primary key field in
related object - must be unique value in OneToOne relationship
- boolean unique() default false
- is foreign key unique within table
- boolean nullable() default true
- is foreign key allowed to be null
- boolean insertable() default true
- is foreign key present on create
- boolean updatable() default true
- is foreign key included in updates
- String columnDefinition() default
- exact DDL for column type
- String table() default
- used for multi-table mappings
11_at_JoinColumns Annotation
- public interface JoinColumns extends ...
- public abstract JoinColumn value()
- defines array of foreign key columns for
composite keys - Example Usage
- _at_OneToOne
- _at_JoinColumns(
- _at_JoinColumn(...),
- _at_JoinColumn(...)
- )
12OneToOne Uni-directional Example Usage
- //create owning side
- ejava.examples.orm.rel.annotated.Person person
new Person() - person.setFirstName("john")
- person.setLastName("doe")
- person.setPhone("410-555-1212")
-
- //create the inverse side
- ejava.examples.orm.rel.annotated.Photo photo
new Photo() - photo.setImage(image)
- //add photo to person anytime
- person.setPhoto(photo) //will set the FK in
person - //write person to DB persist configured to
cascade to photo - em.persist(person)
- //verify what we can get from DB
- em.flush() //FK physically written at
this point - em.clear()
13Primary Key Join Example DB Schema
- create table ORMREL_BORROWER (BORROWER_ID bigint
not null, startDate date, endDate 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
14Primary Key Join
- public interface PrimaryKeyJoinColumn extends
... - used instead of JoinColumn to define a primary
key join between this and referenced object - String name() default
- used when this class defines a composite primary
key - names primary key column in this class' table
that references related object - String referencedColumnName() default
- used to specify column foreign key is referencing
- will default to PK field for related object's
table - String columnDefinition() default
- specific DDL for column definition
15Primary Key Join Example Java Mapping
- _at_Entity _at_Table(name"ORMREL_BORROWER")
- public class Borrower
- _at_Id _at_Column(name"BORROWER_ID")
- private long id
-
- _at_OneToOne(fetchFetchType.LAZY,
optionalfalse, - cascadeCascadeType.PERSIST,
- CascadeType.REFRESH,
- CascadeType.MERGE)
- _at_PrimaryKeyJoinColumn
- private Person identity
- _at_Entity
- _at_Table(name"ORMREL_PERSON")
- public class Person
- _at_Id _at_GeneratedValue _at_Column(name"PERSON_ID")
- private long id
16OneToOne Bi-directional Example DB Schema
- create table ORMREL_BORROWER (BORROWER_ID bigint
not null, startDate date, endDate date,
primary key (BORROWER_ID)) - create table ORMREL_APPLICANT (id bigint
generated by default as identity, APP_PERSON
bigint not null, APP_BORROWER bigint, primary
key (id), unique (APP_PERSON)) - alter table ORMREL_APPLICANT add constraint
FKD186081242E2AFD5 foreign key (APP_PERSON)
references ORMREL_PERSON
RDBMSes are insensitive to direction. Therefore
once a foreign key is defined on one side or the
other, it can be used from either direction
17OneToOne Bi-directional Example Java Mapping
- _at_Entity _at_Table(name"ORMREL_BORROWER")
- public class Borrower
- private static Log log LogFactory.getLog(Bor
rower.class) - _at_Id _at_Column(name"BORROWER_ID")
- private long id
-
- _at_OneToOne(fetchFetchType.LAZY,
- optionaltrue,
- mappedBy"borrower") private
Applicant application - _at_Entity _at_Table(name"ORMREL_APPLICANT")
- public class Applicant
- _at_Id _at_GeneratedValue
- private long id
-
- _at_OneToOne(optionaltrue)
- _at_JoinColumn(name"APP_BORROWER")
- private Borrower borrower
-
Inverse-side of relationship defines remote
property that defines the database mapping
Owning side uses a JoinColumn to name the
foreign key column for the relationship
18OneToOne Bi-directional Example Usage
- Applicant applicant2 em.find(Applicant.class,
applicantId) - Borrower borrower2 em.find(Borrower.class,
borrowerId) - //form relationship
- borrower2.setApplication(applicant2) //set
inverse side - applicant2.setBorrower(borrower2) //set
owning side - em.flush()
- em.clear()
-
- //locate them from DB
- Applicant applicant3 em.find(Applicant.class,
applicantId) - Borrower borrower3 em.find(Borrower.class,
borrowerId) - assertEquals(applicant3.getId(),
borrower3.getApplication().getId()) - assertEquals(borrower3.getId(),
applicant3.getBorrower().getId())
Must manually set both sides of a
bi-directional relationship
19Bi-directional Relationships and Ownership
- Only changes made to owning side impact database
- persist
- update
- remove
- Actions taken on owning side not automatically
propagated to inverse side - must either
- manually update inverse side
- design classes to handle propogation
- refresh object if proper state in database
20Bi-directional Relationships and Ownership Example
- Borrower borrower em.find(Borrower.class,
borrowerId) - Applicant applicant em.find(Applicant.class,
applicantId) - assertNull("borrower has unexpected applicant"
- borrower.getApplication(),
- borrower.getApplication())
- assertNull("applicant has unexpected borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
- //set ONLY the inverse side of the
relationship - borrower.setApplication(applicant)
- assertNotNull("borrower does not have applicant",
- borrower.getApplication())
- assertNull("applicant has unexpected borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
Not setting owning side will cause no later
change to DB
Set on either side does NOT get automatically
propagated to other side
21Bi-directional Relationships and Ownership
Example (cont.)
- em.getTransaction().commit()
- em.clear()
- assertFalse("borrower was managed",
em.contains(borrower)) - assertFalse("application was managed",
em.contains(applicant)) -
- borrower em.find(Borrower.class, borrowerId)
- applicant em.find(Applicant.class,
applicantId) - //verify that relationship from cache
never written to DB - assertNull("borrower has unexpected applicant"
- borrower.getApplication(),
- borrower.getApplication())
- assertNull("applicant has unexpected borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
Not setting owning side caused change to be lost
on next refresh
22Bi-directional Relationships and Ownership Example
- Borrower borrower em.find(Borrower.class,
borrowerId) - Applicant applicant em.find(Applicant.class,
applicantId) - assertNull("borrower has unexpected applicant"
- borrower.getApplication(),
- borrower.getApplication())
- assertNull("applicant has unexpected borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
- //set ONLY the owning side of the
relationship - applicant.setBorrower(borrower)
- assertNull("borrower has unexpected applicant",
- borrower.getApplication())
- assertNotNull("applicant does not have borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
Not setting inverse side no impact to DB
Set on owning side will take care of updating DB
23Bi-directional Relationships and Ownership
Example (cont.)
- em.getTransaction().commit()
- em.clear()
- assertFalse("borrower was managed",
em.contains(borrower)) - assertFalse("application was managed",
em.contains(applicant)) -
- borrower em.find(Borrower.class, borrowerId)
- applicant em.find(Applicant.class,
applicantId) - //verify that relationship from cache
written to DB - assertNotNull("borrower did not have applicant"
- borrower.getApplication(),
- borrower.getApplication())
- assertNotNull("applicant did not have borrower"
- applicant.getBorrower(),
- applicant.getBorrower())
Changes now correctly reflected in DB
24ManyToOne
- Uni-directional
- Many side is only class (owner) that knows of
the relationship - uses _at_ManyToOne annotation
- this class defines the mapping to the database
- may use _at_JoinColumn or _at_JoinTable annotation
- Bi-directional
- Many side required to own relationship
- One side (inverse side) names the owner's
property that defines the relationship - may use _at_OneToMany(mappedByremote property)
25ManyToOne
- Uni-directional
- Bi-Directional
26ManyToOne Uni-directional Example DB Schema
- create table ORMREL_MEDIA (MEDIA_ID bigint
generated by default as identity, title
varchar(255), primary key (MEDIA_ID))
- create table ORMREL_MEDIACOPY (COPY_NO integer
not null, MEDIACOPY_MID bigint not null,
primary key (COPY_NO, MEDIACOPY_MID)) - alter table ORMREL_MEDIACOPY add constraint
FKCDB47669F152B359 foreign key (MEDIACOPY_MID)
references ORMREL_MEDIA
Many side defines foreign key to one side
In this case, foreign key is part of primary
key
27ManyToOne Uni-directional Example Java Mapping
There is no inverse relationship definition for
uni-directional relationships
- _at_Entity _at_Table(name"ORMREL_MEDIA")
- public class Media
- _at_Id _at_GeneratedValue _at_Column(name"MEDIA_ID")
- private long id
- _at_Entity _at_Table(name"ORMREL_MEDIACOPY")
- _at_IdClass(MediaCopyPK.class)
- public class MediaCopy
- //use m2o to have media automatically
associated - _at_ManyToOne(optionalfalse)
- _at_JoinColumn(
- name"MEDIACOPY_MID",
- nullablefalse, updatablefalse)
- public Media getMedia()
return media - private void setMedia(Media media)
this.media media
Owning side uses a JoinColumn to name the
foreign key column for the relationship
28_at_ManyToOne Annotation
- public interface ManyToOne extends ...
- Class targetEntity() default void.class
- entity type to map target of relation
- can be determined by provider from property type
- CascadeType cascade() default
- defines which persistence operations will also
apply to related object - FetchType fetch() default EAGER
- hint to provider whether related object is
necessary - boolean optional() default true
- is foreign key allowed to be null
29ManyToOne Uni-directional Example Usage
- //create one (inverse side)
- ejava.examples.orm.rel.annotated.Media media
new Media() - media.setTitle("EJB3")
- em.persist(media)
- assertTrue(media.getId() ! 0)
-
- //create some copies (owning side)
- for(int i0 ilt5 i)
- ejava.examples.orm.rel.annotated.MediaCopy mc
- new MediaCopy(media, i)
- assertNotNull(mc.getMedia())
- assertEquals(i, mc.getCopyNo())
- //must manually persist since no cascade
defined from Media - em.persist(mc)
- log.info("created copy" mc)
Owning side property set in ctor()
30ManyToOne Uni-directional Example Usage (cont.)
- em.flush()
- em.clear()
-
- //verify media copy from database provides media
object - for(int i0 ilt5 i)
- //media copy uses a composite primary key
- ejava.examples.orm.rel.MediaCopyPK pk
- new MediaCopyPK(media.getId(), i)
- MediaCopy mc em.find(MediaCopy.class, pk)
- assertNotNull("media copy not found" pk,
mc) - assertEquals("unexpected mediaId"
mc.getMediaId(), - mc.getMediaId(), media.getId())
- assertEquals("unexpected copyNo"
mc.getCopyNo(), - mc.getCopyNo(), i)
- assertNotNull("no media resolved",
mc.getMedia())
31Issues Mapping Composite Key Column
- public class MediaCopyPK implements Serializable
- _at_Column(name"COPY_NO") public int
getCopyNo() ... - _at_Column(name"MEDIACOPY_MID") public long
getMediaId() ... - _at_Entity _at_Table(name"ORMREL_MEDIACOPY")
- _at_IdClass(MediaCopyPK.class)
- public class MediaCopy
- public int getCopyNo()
return copyNo - private void setCopyNo(int copyNo)
this.copyNo copyNo -
- //this property is used for the composite
primary key - public long getMediaId()
return mediaId - private void setMediaId(long mediaId)
- this.mediaId mediaId
create table ORMREL_MEDIACOPY (COPY_NO integer
not null, MEDIACOPY_MID bigint not null,
primary key (COPY_NO, MEDIACOPY_MID))
Provider requires a separate property to map
entire foreign key.
32Issues Mapping Composite Key Column
- _at_ManyToOne
- _at_JoinColumn(name"MEDIACOPY_MID")
- public Media getMedia()
return media - _at_SuppressWarnings("unused")
- private void setMedia(Media media)
this.media media - javax.persistence.PersistenceException
org.hibernate.MappingException Repeated column
in mapping for entity ejava.examples.orm.rel.anno
tated.MediaCopy column MEDIACOPY_MID (should be
mapped with insert"false" update"false") - _at_ManyToOne
- _at_JoinColumn(name"MEDIACOPY_MID",
- insertablefalse,updatablefalse)
- public Media getMedia()
return media
alter table ORMREL_MEDIACOPY add constraint
FKCDB47669F152B359 foreign key (MEDIACOPY_MID)
references ORMREL_MEDIA
Foreign key for the relationship is mapped to
same column as primary key
Now updates to the media property do not
attempt to update the FK column, which is also a
PK column.
33ManyToOne Bi-directional Example DB Schema
- create table ORMREL_BORROWER (BORROWER_ID bigint
not null, startDate date, endDate 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
Many side defines foreign key to one side
34ManyToOne Bi-directional Example Java Mapping
- _at_Entity _at_Table(name"ORMREL_BORROWER")
- public class Borrower
- _at_Id _at_Column(name"BORROWER_ID")
- private long id
-
- _at_OneToMany(mappedBy"borrower",
- fetchFetchType.LAZY)
- private CollectionltCheckoutgt checkouts
new ArrayListltCheckoutgt() - _at_Entity _at_Table(name"ORMREL_CHECKOUT")
- public class Checkout
- private static long CHECKOUT_DAYS
100060602414 - _at_Id _at_GeneratedValue _at_Column(name"CHECKOUT_ID"
) - private long id
- _at_ManyToOne(optionalfalse)
- _at_JoinColumn(name"CHECKOUT_BID")
- private Borrower borrower
Inverse-side of relationship defines remote
property that defines the database mapping
Owning side uses a JoinColumn to name the
foreign key column for the relationship
35_at_OneToMany Annotation
- public interface OneToMany extends ...
- Class targetEntity() default void.class
- defines mapped type of related object
- provider can determine value from property type
- CascadeType cascade() default
- defines which persistence operations propagate to
related object - FetchType fetch() default EAGER
- hint to provider whether related object is always
necessary - String mappedBy() default
- names property in related object that defines
mapping to database
36ManyToOne Bi-directional Example Usage
- //get a borrower
- ejava.examples.orm.rel.annotated.Borrower
borrower - em.find(Borrower.class, borrowerId)
- assertNotNull(borrower)
- assertTrue(borrower.getCheckouts().size() 0)
-
- //create 1st checkout
- ejava.examples.orm.rel.annotated.Checkout
checkout - new Checkout(new Date())
- checkout.setBorrower(borrower) //set owning side
- borrower.addCheckout(checkout) //set inverse
side - //above is a wrapper around
- // borrower.getCheckouts().add(checkout)
- em.persist(checkout) //persist owning side of
the relation
37ManyToOne Bi-directional Example Usage (cont.)
- //create a couple more
- for(int i0 ilt5 i)
- Checkout co new Checkout(new Date())
- co.setBorrower(borrower) //set owning side
- borrower.addCheckout(co) //set inverse
side - //above is a wrapper around
- // borrower.getCheckouts().add(checkout)
- em.persist(co) //persist owning side of the
relation -
- log.info("done populating borrower")
-
- em.flush()
- em.clear()
- Borrower borrower2 em.find(Borrower.class,
borrower.getId()) - assertEquals(6, borrower2.getCheckouts().size())
38Changing a Collection
- Wrong!
- don't replace the collection from the managed
object with a transient one - CollectionltCheckoutgt newCheckouts ...
- borrower.setCheckouts(newCheckouts)
//WRONG! - Correct!
- update the existing collection within the managed
object - CollectionltCheckoutgt newCheckouts ...
- borrower.getCheckouts().addAll(newCheckouts)
39OneToMany
40OneToMany
- Uni-directional
- One side (owner) is only class that knows of
relationship - uses _at_OneToMany annotation
- This class defines mapping to database using a
Join (or link) table - must use a _at_JoinTable
- Bi-directional
- same as ManyToOne bi-directional
41Link/Join Table
42OneToMany Uni-directional Example DB 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, unique
(media_MEDIA_ID)) - create table ORMREL_MEDIA (MEDIA_ID bigint
generated by default as identity, title
varchar(255), primary key (MEDIA_ID)) - alter table ORMREL_INVENTORY_MEDIA add constraint
FKF6FA5C317DD5E49D foreign key (ORMREL_INVENTORY_i
d) references ORMREL_INVENTORY - alter table ORMREL_INVENTORY_MEDIA add constraint
FKF6FA5C31A70D4E48 foreign key (media_MEDIA_ID)
references ORMREL_MEDIA
Link table declares foreign keys to form
relationships
Unique media ID guards against a single media
being mapped to multiple inventories
43OneToMany Uni-directional Example Java Mapping
Inverse-side of relationship defines nothing
for uni-directional relationships
- _at_Entity _at_Table(name"ORMREL_MEDIA")
- public class Media
- _at_Id _at_GeneratedValue _at_Column(name"MEDIA_ID")
- private long id
- _at_Entity _at_Table(name"ORMREL_INVENTORY")
- public class Inventory
- private long id
- private String name
- private CollectionltMediagt media new
ArrayListltMediagt() -
- _at_OneToMany(cascadeCascadeType.ALL)
- _at_JoinTable(name"ORMREL_INVENTORY_MEDIA")
- public CollectionltMediagt getMedia()
- return media
-
- public void setMedia(CollectionltMediagt media)
- this.media media
-
Owning side of OneToManyuni-directional must
define a link table to hold relationship
44_at_JoinTable Annotation
- public interface JoinTable extends ...
- very similar to _at_Table annotation
- String name() default
- table name for join table
- String catalog() default
- catalog name for join table
- String schema() default
- schema name for join table
- JoinColumn joinColumns() default
- array of columns defining foreign key to this
object - JoinColumn inverseJoinColumns() default
- array of columns defining foreign key to related
object - UniqueConstraint uniqueConstraints()
45OneToMany Uni-directional Example Usage
- //create owning side
- ejava.examples.orm.rel.annotated.Inventory
inventory1 - new Inventory()
- inventory1.setName("testLinkFind")
- em.persist(inventory1)
-
- //create and relate inverse side
- for(int i0 ilt5 i)
- ejava.examples.orm.rel.annotated.Media media
new Media() - em.persist(media)
- log.info("created media" media)
- //relation defined to cascade perists
- inventory1.getMedia().add(media)
46OneToMany Uni-directional Example Usage (cont.)
- em.getTransaction().commit()
- em.clear()
- em.getTransaction().begin()
- //get new copy of inventory from DB
- assertFalse("inventory still managed",em.contains(
inventory1)) - Inventory inventory2 em.find(Inventory.class,
inventory1.getId()) - assertNotNull("inventory not found", inventory2)
- assertNotSame(inventory1, inventory2)
- assertNotNull("media null", inventory2.getMedia())
- assertEquals("unexpected media count"
inventory2.getMedia().size(), - inventory1.getMedia().size(),
inventory2.getMedia().size())
47Using FK in uni-directional OneToMany
- create table ORMO2M_CHILD (
- CHILDID bigint generated by default as
identity (start with 1), - name varchar(255),
- PARENT_ID bigint,
- primary key (CHILDID)
- )
-
- create table ORMO2M_PARENT (
- PARENTID bigint generated by default as
identity (start with 1), - name varchar(255),
- primary key (PARENTID)
- )
- alter table ORMO2M_CHILD
- add constraint FK257187DDF37CA975
- foreign key (PARENT_ID)
- references ORMO2M_PARENT
48Using FK in uni-directional OneToMany
- _at_Entity(name"O2MChild") _at_Table(name"ORMO2M_CHILD
") - public class OneManyChild
- _at_Id _at_GeneratedValue _at_Column(name"CHILDID")
- public long getId()
- return id
-
- _at_Entity(name"O2MOwningParent")
_at_Table(name"ORMO2M_PARENT") - public class OneManyOwningParent
- _at_Id _at_GeneratedValue _at_Column(name"PARENTID")
- public long getId()
- return id
-
- _at_OneToMany
- _at_JoinColumn(name"PARENT_ID")
- //_at_JoinTable(
- // joinColumns_at_JoinColumn(name"PARENT
_ID"))
49OneToMany bi-directional
- The same as ManyToOne bi-directional
- Spec requires _at_ManyToOne side of a bi-directional
relationship be the owner
50ManyToMany
- Uni-directional
- Bi-Directional
51ManyToMany
- Uni-directional
- One class (owner) knows about relationship
- uses _at_ManyToMany annotation
- Owner defines mapping to database using a Join
(or link) table - uses _at_JoinTable
- Bi-directional
- Both classes know of relationship
- use _at_ManyToMany annotation
- Owning side defines mapping to database
- uses _at_JoinTable
- Invserse side names property on remote end that
defines mapping - uses _at_ManyToMany(mappedByremote property)
52ManyToMany Uni-directional Example DB Schema
- create table ORMREL_WANTED (id bigint generated
by default as identity, primary key (id)) - create table ORMREL_WANTED_MEDIA
(ORMREL_WANTED_id bigint not null,
media_MEDIA_ID bigint not null) - create table ORMREL_MEDIA (MEDIA_ID bigint
generated by default as identity (start with 1),
title varchar(255), primary key (MEDIA_ID)) - alter table ORMREL_WANTED_MEDIA add constraint
FKEE528304A70D4E48 foreign key (media_MEDIA_ID)
references ORMREL_MEDIA - alter table ORMREL_WANTED_MEDIA add constraint
FKEE52830486AFC6B6 foreign key (ORMREL_WANTED_id)
references ORMREL_WANTED
Link table declares foreign keys to form
relationships
No unique ID guards for many to many mapping
53ManyToMany Uni-directional Example Java Mapping
Inverse-side of relationship defines nothing
for uni-directional relationships
- _at_Entity _at_Table(name"ORMREL_MEDIA")
- public class Media
- _at_Id _at_GeneratedValue _at_Column(name"MEDIA_ID")
- private long id
- _at_Entity _at_Table(name"ORMREL_WANTED")
- public class WantList
- private long id
- private CollectionltMediagt media new
ArrayListltMediagt() -
- _at_Id _at_GeneratedValue
- public long getId() return id
- public void setId(long id) this.id id
- _at_ManyToMany
- _at_JoinTable(name"ORMREL_WANTED_MEDIA")
public CollectionltMediagt getMedia() return
media - public void setMedia(CollectionltMediagt
media) - this.media media
-
Owning side of ManyToManyuni-directional must
define a link table to hold relationship
54_at_ManyToMany Annotation
- public interface OneToMany extends ...
- identical properties as OneToMany
- Class targetEntity() default void.class
- defines mapped type of related object
- provider can determine value from property type
- CascadeType cascade() default
- defines which persistence operations propagate to
related object - FetchType fetch() default EAGER
- hint to provider whether related object is always
necessary - String mappedBy() default
- names property in related object that defines
mapping to database
55ManyToMany Uni-directional Example Usage
- CollectionltWantListgt wantLists new
ArrayListltWantListgt() - CollectionltMediagt media new ArrayListltMediagt()
- //create owning side objects
- for(int i0 ilt3 i)
- ejava.examples.orm.rel.annotated.WantList
wanted - new WantList()
- em.persist(wanted)
- wantLists.add(wanted)
-
- //create inverse side objects
- for (int i0 ilt2 i)
- ejava.examples.orm.rel.annotated.Media
mediaItem - new Media()
- mediaItem.setTitle("title " i)
- em.persist(mediaItem) //rel not defined to
cascade - media.add(mediaItem)
56ManyToMany Uni-directional Example Usage (cont.)
- //relate the inverse-side objects to owning
objects - for(WantList w wantLists)
- for(Media m media)
- //we can only navigate this in one
direction - w.getMedia().add(m)
-
-
-
- em.getTransaction().commit()
- em.clear()
57ManyToMany Uni-directional Example Usage (cont.)
- //verify we don't have them in the cache
- for (WantList w wantLists)
- assertFalse("want list still managed",
em.contains(w)) -
- for (Media m media)
- assertFalse("media still managed",
em.contains(m)) -
- for (WantList w wantLists)
- WantList wanted em.find(WantList.class,
w.getId()) - assertNotNull("want list not found"
w.getId(), wanted) - assertEquals("unepxected number of media",
- wanted.getMedia().size(),
media.size())
58ManyToMany Bi-directional Example DB Schema
- create table ORMREL_AUTHOR (id bigint generated
by default as identity, name varchar(255),
primary key (id)) - create table ORMREL_AUTHOR_MEDIA (LINK_AUTHOR_ID
bigint not null, LINK_MEDIA_ID bigint not null) - create table ORMREL_MEDIA (MEDIA_ID bigint
generated by default as identity, title
varchar(255), primary key (MEDIA_ID)) - alter table ORMREL_AUTHOR_MEDIA add constraint
FKA0D2B4E09B01C6F2 foreign key (LINK_MEDIA_ID)
references ORMREL_MEDIA - alter table ORMREL_AUTHOR_MEDIA add constraint
FKA0D2B4E089FFE922 foreign key (LINK_AUTHOR_ID)
references ORMREL_AUTHOR
Link table declares foreign keys to form
relationships
No unique ID guards for many to many mapping
59ManyToMany Bi-directional Example Java Mapping
Inverse-side of relationship defines property
on remote end that defines relationship
- _at_Entity _at_Table(name"ORMREL_MEDIA")
- public class Media
- _at_ManyToMany(mappedBy"media")
- private CollectionltAuthorgt authors new
ArrayListltAuthorgt() - _at_Entity _at_Table(name"ORMREL_AUTHOR")
- public class Author
- private ListltMediagt media new
ArrayListltMediagt() - _at_ManyToMany
- _at_JoinTable(name"ORMREL_AUTHOR_MEDIA",
joinColumns _at_JoinColumn(n
ame"LINK_AUTHOR_ID"),
inverseJoinColumns _at_JoinColumn(name"LINK_MED
IA_ID")) - _at_OrderBy("title DESC")
- public ListltMediagt getMedia() return media
- public void setMedia(ListltMediagt media)
this.mediamedia
Owning side of ManyToManybi-directional must
define a link table to hold relationship
60ManyToMany Bi-directional Example Usage
- CollectionltAuthorgt authors new
ArrayListltAuthorgt() - CollectionltMediagt media new ArrayListltMediagt()
- for(int i0 ilt5 i)
- ...
- em.persist(author)
- authors.add(author)
-
- for (int i0 ilt3 i)
- ...
- em.persist(mediaItem)
- media.add(mediaItem)
-
- for(Author a authors)
- for(Media m media)
- a.getMedia().add(m)
- m.getAuthors().add(a)
-
set both sides of a bi-directional relationship
61Map-based Collection
62Map-based Collection Example DB Schema
- create table ORMREL_LIBRARY (id bigint generated
by default as identity, primary key (id)) - create table ORMREL_LIBRARY_ORMREL_BORROWER
(ORMREL_LIBRARY_id bigint not null,
borrowers_BORROWER_ID bigint not null, primary
key (ORMREL_LIBRARY_id, borrowers_BORROWER_ID),
unique (borrowers_BORROWER_ID)) - create table ORMREL_BORROWER (BORROWER_ID bigint
not null, startDate date, endDate date,
primary key (BORROWER_ID)) - alter table ORMREL_LIBRARY_ORMREL_BORROWER add
constraint FKE21AB3BE9B36B899 foreign key
(borrowers_BORROWER_ID) references
ORMREL_BORROWER - alter table ORMREL_LIBRARY_ORMREL_BORROWER add
constraint FKE21AB3BE7A0BF0FD foreign key
(ORMREL_LIBRARY_id) references ORMREL_LIBRARY
Link Table declares foreign keys to form
relationships
63Map-based Collection Example Java Mapping
- _at_Entity _at_Table(name"ORMREL_LIBRARY")
- public class Library
- private long id
- private MapltLong, Borrowergt borrowers
- new HashMapltLong, Borrowergt()
-
- _at_OneToMany(cascadeCascadeType.ALL,
fetchFetchType.LAZY) - _at_MapKey(name"id")
- public MapltLong, Borrowergt getBorrowers()
- return borrowers
-
- public void setBorrowers(MapltLong, Borrowergt
borrowers) - this.borrowers borrowers
-
MapKey defines property from related object to be
used as key in Map
64Map-based Collection Example Usage
- ejava.examples.orm.rel.annotated.Library library
- new Library()
- em.persist(library)
-
- for(int i0 ilt5 i)
- ejava.examples.orm.rel.annotated.Person
person - new Person()
- person.setFirstName("test")
- person.setLastName("MapCreate-" i)
- em.persist(person) //persist now - borrower
copies id - ejava.examples.orm.rel.annotated.Borrower
borrower - new Borrower(person)
- library.getBorrowers().put(borrower.getId(),
borrower)
65CascadeType
- ALL
- combination of all types
- PERSIST
- related objects are automatically managed and
will be persisted to the database when related to
this object - REMOVE
- related objects are deleted from database when
this object is removed - REFRESH
- related objects will have their states pulled
from the database along with this object when it
is refreshed - MERGE
- related objects will have their states written to
the database along with this object when it is
merged
66Summary
- Relationship Sides
- owning side
- inverse side
- Relationship Types
- OneToOne
- uni-directional, bi-directional
- ManyToOne
- uni-directional, bi-directional
- OneToMany
- uni-directional (bi-directional same as
ManyToOne) - ManyToMany
- uni-directional, bi-directional
67Summary (cont.)
- Collection Types
- Collection, Set, List, Map
- FetchType
- EAGER
- LAZY
- CascadeType
- ALL, PERSIST, REMOVE, MERGE, REFRESH
68References
- Enterprise JavaBeans 3.0, 5th Edition Burke
Monsen-Haefel ISBN 0-596-00978-X O'Reilly