Enterprise Java Development@TOPIC@
Determines how when related objects are retrieved
LAZY
Only parent object immediately retrieved -- child objects retrieved on demand
Can result in better performance when child data is not needed - unused objects not retrieved
Can result in poor performance when all child data is needed - retrieves objects one at a time rather than in bulk
Can result in lazy load exception if unloaded data accessed after it can no longer be retrieved from database
Can be simulated, on demand, using value or result class queries with JPA-QL
EAGER (the default)
Provider required to have loaded prior to transaction committing
Can result in better performance when all child data is needed
Can result in poor performance when no child data or limited child data is needed
Can result in poor performance when parent has multiple collections and equivalent of an "EAGER LAZY load" is performed before returning control back to the caller
Should never result in a lazy load exception
Can be simulated, on-demand, using "join fetch" queries with JPA-QL
Figure 58.1. fetch=LAZY Example Declaration
@OneToMany(mappedBy="borrower", //this relationship is owned by Checkout
fetch=FetchType.LAZY)
private Collection<Checkout> checkouts = new ArrayList<Checkout>();
@XxxToXxx Relationship annotated with fetch=LAZY property
Figure 58.2. fetch=LAZY Example Use
Borrower borrower2 = em.find(Borrower.class, borrower.getId());
logger.info("found borrower: {}", borrower.getName());
assertEquals(6, borrower2.getCheckouts().size());
Parent accessed, debug printed, and then child collection accessed
Figure 58.3. fetch=LAZY SQL Output
- 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=? -Borrower@7cfb0c4c, ctor() ... -found borrower: john smith - select checkouts0_.CHECKOUT_BID as CHECKOUT4_16_0_, checkouts0_.CHECKOUT_ID as CHECKOUT1_16_0_, checkouts0_.CHECKOUT_ID as CHECKOUT1_16_1_, checkouts0_.CHECKOUT_BID as CHECKOUT4_16_1_, checkouts0_.outDate as outDate2_16_1_, checkouts0_.returnDate as returnDa3_16_1_ from ORMREL_CHECKOUT checkouts0_ where checkouts0_.CHECKOUT_BID=?
Debug for parent printed before child rows retrieved
Child rows retrieved when parent collection accessed
Other SQL details of Borrower left out of example
Figure 58.4. fetch=EAGER Example Declaration
@OneToMany(mappedBy="borrower", //this relationship is owned by Checkout
fetch=FetchType.EAGER)
private Collection<Checkout> checkouts = new ArrayList<Checkout>();
Figure 58.5. fetch=EAGER Example Use
Borrower borrower3 = em.find(Borrower.class, borrowerId);
logger.info("found borrower: {}", borrower.getName());
assertEquals(0,borrower3.getCheckouts().size());
Same as fetch=LAZY case
Figure 58.6. fetch=EAGER SQL Output
- select borrower0_.BORROWER_ID as BORROWER1_15_0_, borrower0_.endDate as endDate2_15_0_, borrower0_.startDate as startDat3_15_0_, checkouts1_.CHECKOUT_BID as CHECKOUT4_16_1_, checkouts1_.CHECKOUT_ID as CHECKOUT1_16_1_, checkouts1_.CHECKOUT_ID as CHECKOUT1_16_2_, checkouts1_.CHECKOUT_BID as CHECKOUT4_16_2_, checkouts1_.outDate as outDate2_16_2_, checkouts1_.returnDate as returnDa3_16_2_ from ORMREL_BORROWER borrower0_ left outer join ORMREL_CHECKOUT checkouts1_ on borrower0_.BORROWER_ID=checkouts1_.CHECKOUT_BID where borrower0_.BORROWER_ID=? -found borrower: john smith
Child objects fetched with parent
Access to child collection occurs after all children fetched
fetch=EAGER is only a promise to fetch the related object before returning from the call. It does not always mean that a SQL JOIN was performed. There are times (e.g., fetch=EAGER on multiple collections within parent) when the provider will perform a SQL JOIN for some of the relationships and the functional equivalent of a fetch=LAZY for the remaining relationships.
Although useful at times, avoid haphazard use of fetch=EAGER when defining relationships and look to rely on custom queries to do this behavior instead. It is very easy to turn a fetch=LAZY into EAGER at query time. It is very difficult to do the opposite. Custom queries also allow us to incrementally build a complex parent object tree an optimized query at a time. fetch=EAGER will do the same, but may perform 10s, 100s, 1000s of more unoptimized queries of child rows we may never need.
Automatically cause persistence commands to be repeated on related objects
Figure 58.7.
@Entity
@Table(name="RELATIONEX_LICAPP")
public class LicenseApplication {
@Id @GeneratedValue
private int id;
@Temporal(TemporalType.TIMESTAMP)
private Date updated;
@OneToOne(optional=false, fetch=FetchType.EAGER,
cascade={
CascadeType.PERSIST,
CascadeType.DETACH,
CascadeType.REMOVE,
CascadeType.REFRESH,
CascadeType.MERGE
})
private License license;
PERSIST
Related entities persisted when this entity is passed to em.persist()
DETACH
Related entities detached from persistence unit when this entity passed to em.detach()
REMOVE
Related entities deleted from database when this entity passed to em.remove()
REFRESH
Related entities refreshed with state of database when this entity passed to em.refresh()
MERGE
Related entities update state of database when this entity is passed to em.merge()
Automatic removal of an object who's sole purpose is to support a related object that may dereference it
Related to cascade=REMOVE but the triggering object is not being deleted
Supported in the following relationships
@OneToOne
@OneToMany
Figure 58.8. OrphanRemoval Example Declaration
@Entity
@Table(name="RELATIONEX_ATTENDEE")
public class Attendee {
@Id @GeneratedValue
private int id;
//orphanRemoval will take care of dereference and DELETE from dependent Attendee
@OneToOne(cascade=CascadeType.PERSIST, orphanRemoval=true)
private Residence residence;
Figure 58.10. OrphanRemoval Database Interaction
Hibernate: update RELATIONEX_ATTENDEE set name=?, residence_id=? where id=? Hibernate: delete from RELATIONEX_RESIDENCE where id=?
Attendee.residence_id set to null
Orphaned residence deleted
Use fetch=EAGER when always accessing related objects together
Use fetch=LAZY when commonly access one object without accessing related objects
Use JPA-QL when encountering corner cases that violate default mapping
Cascades can be used to automate persistence actions on an entire object graph
OrphanRemoval can used to automatically delete dereferenced objects that have no use outside the scope of its owning relation