Eclipselink 2.1 is a persistence provider runtime of the Java Persistence API 2.1 specification. JPA specification defines two major strategies of loading data (Lazy and Eager). The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first time accessed.
The Lazy loading is commonly used to defer the loading of the attributes or associations of an entity or entities until the point at which they are needed, meanwhile the eager loading is an opposite concept in which the attributes and associations of an entity or entities are fetched explicitly and without any need for pointing them.
Determine The Loading Strategy
EclipseLink 2.1 provides you different ways to mention your desired loading strategy.
- Basic Annotation: It provides a fetchType attribute that can be used for determining the strategy to which the attribute(s) that should be loaded eagerly or lazily.
- @OneToOne, @ManyToOne, @OneToMany and @ManyToMany: Every associations annotations provide fetchType attribute that can be used for determining the strategy to which the attributes(s) that should be loaded eagerly or lazily.
Lazy Fetch Type With @OneToOne and @ManyToOne
If you’ve noted before @OneToOne association example, you’ve absolutely seen the association between Employee and Address entities adhered a bidirectional OneToOne. It’s the time to give more attention about Eager and Lazy loading in that association. Let’s see what’s the role that can fetchType attribute play in this association when it used at the Employee side and at the Address side. (see EclipseLink Tutorials)
The default value of fetchType attribute within OneToOne annotation is an eager value, and whether the value of the field or property should be lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the value must be eagerly fetched and the LAZILY is just a hint to the persistence provider runtime.
By default EclipseLink consider the OneToOne and ManyToOne associations as not lazily associations. So if we’ve set OneTOne or ManyToOne associations to lazy without using of EclipseLink weaving, we surely have an eager association. Let’s see Figure 1.0 that shows you an Employee entity associated with an Address entity in a OneToOne association.
Employee.java
package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="EmployeeType") @DiscriminatorValue(value="EMP") @EntityListeners(value={PersistenceContextListener.class}) public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @Embedded private EmployeePeriod employeePeriod; @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.EAGER) private List phones; @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER) @JoinTable( name="employee_project", joinColumns={@JoinColumn(name="employeeId")}, inverseJoinColumns={ @JoinColumn(table="project",name="projectId"), @JoinColumn(table="localproject",name="projectId"), @JoinColumn(table="globalproject",name="projectId")} ) private List projects; @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class) @JoinColumns({ @JoinColumn(name="AddressId",referencedColumnName="AddressId"), @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"), @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")} ) private Address address; // Note: OneToOne annotation is configured as lazy loading public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public List getPhones() { return phones; } public void setPhones(List phones) { this.phones = phones; } public List getProjects() { return projects; } public void setProjects(List projects) { this.projects = projects; } public EmployeePeriod getEmployeePeriod() { return employeePeriod; } public void setEmployeePeriod(EmployeePeriod employeePeriod) { this.employeePeriod = employeePeriod; } }
Address.java
package net.javabeat.eclipselink.data; import javax.persistence.CascadeType; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.OneToOne; import javax.persistence.PrePersist; @Entity(name="address") @EntityListeners(value={PersistenceContextListener.class}) public class Address { @EmbeddedId private AddressPK addressId; private String addressCountry; private String addressCity; @OneToOne(cascade=CascadeType.ALL,mappedBy="address",fetch=FetchType.LAZY) private Employee employee; // Note: The OneToOne association is configured as lazy loading public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public String getAddressCountry() { return addressCountry; } public void setAddressCountry(String addressCountry) { this.addressCountry = addressCountry; } public String getAddressCity() { return addressCity; } public void setAddressCity(String addressCity) { this.addressCity = addressCity; } public AddressPK getAddressId() { return addressId; } public void setAddressId(AddressPK addressId) { this.addressId = addressId; } @PrePersist public void prePersist(){ System.out.println("Address Entity :: Method PrePersist Invoked Upon Entity "+this); } }
Let’s we execute the following snippet of code:
Employee emp = em.find(Employee.class, 1);
- The executed sample code assumed that an employee row instance has been persisted previously. (See OneToOne Association).
- The Eclipselink implementation will discard the hint that you’ve set that the fetch type is lazy.
- The Address OneToOne mapping will be fetched as an Eager loading. See Figure 1.0 that shows you the instance of Employee and its dependent objects.
Figure 1.0
As you’ve noted in the Figure 1.0
- The Address entity has fetched by the EclipseLink JPA, although the fetch type of Address is lazy.
- By default, EclipseLink discard the fetch Type lazy in case we’ve used the @OneToOne and @ManyToOne.
Enforce EclipseLink To Consider Lazy Associations
To enforce EclipseLink considering the Lazy fetch type that passed in the previous Employee sample, we have to use what Eclipse named “Weaved Indirection”.
If we set OneToOne or ManyToOne associations are lazy, we enable weaving, the EclipseLink JPA persistence provider will use weaving to enable value holder indirection for these associations. Before any further next lines let’s see how could we enable weaving using Eclipse IDE and by using the same provided examples.
- Determine the eclipselink.jar that contains the weaving required API (See EclipseLink Installation And Configuration Using Eclipse)
- Open EclipseIDE.
- Open EclipseLink – JPA-Installation package
- Determine you executable class (At this example it will be JPAImpl.java).
- Set a break point at that line code that fetched Employee instance from a database.
- Click on the Run menu from the top main menu.
- Select Debug Configuration.
- From the window opened and from the left side select the Java Application that you would use for weaving.
- From the same window, but at the right side select the titled tab (Arguments).
- Inside the VM arguments box type (-javaagent:eclipselink.jar).
- From the Working Directory beneath the VM arguments box select Other.
- Specify the eclipselink.jar file by navigating into the installed EclipseLink Installation file. See Figure 1.1 that shows you the final image of what you should have on your local environment.
Figure 1.1
- Close the dialog by clicking on the Debug.
- You should see the JPAImpl.java is executing and the debug stopped in at the break point that already defined.
- Click F6 for proceed one step forward.
- Let your mouse curser move over the emp instance.
- The EclipseIDE must display the emp instance and all of its dependent objects, but that’s time with respective for those mapping that defined as LAZY LOADING. See Figure 1.2.
Figure 1.2
- Eclipselink uses the indirection; the indirection is likely that a graph of persistent objects that contain (UN triggered) indirection objects.
- EclipseLink uses weaving to implement lazy fetching for OneToOne and ManyToOne associations.
- If you’ve selected the value holder that represents the address instance that named (_persistence_address_vh=UnitOfWorkQueryValueHolder) than you should see the following message shown below {UnitOfWorkQueryValueHolder: not instantiated}
- Try to investigate the address value holder for seeing the other attributes such as isLazy (true), isInstantiated(false) and others.
- Also, the address attribute that belongs to employee instance is referring to null. See Figure 1.3.
Figure 1.3
- Address instance that belongs to Employee is Null, cause the fetch type that has been selected is Lazy load for achieving such that association.The only way that permitted for accessing the address is by calling it directly. See Figure 1.4.
Figure 1.4
- As you’ve noted the address instance that belongs to Employee does respond now.
By Default EclipseLink Support Lazy Load for @OneToMany and @ManyToMany
Let’s look at the Employee entity and its dependent either objects (Projects and Phones), where the Employee does associate with the Projects as ManyToMany and it does associate with Phones as OneToMany. Let’s see how could be achieving lazy load easily as opposite of using it with OneToOne and ManyToOne.
Employee.java
package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="EmployeeType") @DiscriminatorValue(value="EMP") @EntityListeners(value={PersistenceContextListener.class}) public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @Embedded private EmployeePeriod employeePeriod; @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.LAZY) private List phones; // OneToMany lazy Load Association @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinTable( name="employee_project", joinColumns={@JoinColumn(name="employeeId")}, inverseJoinColumns={ @JoinColumn(table="project",name="projectId"), @JoinColumn(table="localproject",name="projectId"), @JoinColumn(table="globalproject",name="projectId")} ) private List projects; // ManyToMany Lazy Load Association @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class) @JoinColumns({ @JoinColumn(name="AddressId",referencedColumnName="AddressId"), @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"), @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")} ) private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public List getPhones() { return phones; } public void setPhones(List phones) { this.phones = phones; } public List getProjects() { return projects; } public void setProjects(List projects) { this.projects = projects; } public EmployeePeriod getEmployeePeriod() { return employeePeriod; } public void setEmployeePeriod(EmployeePeriod employeePeriod) { this.employeePeriod = employeePeriod; } }
- Employee Entity does consider two additional associations one of type OneToMany with Phone entity and the second is ManyToMany with the Project entity.
- By default EclipseLink does consider the lazy loading of such these associations, so no need for any further configuration as we’ve seen in the OneToOne and ManyToOne. See Figure 1.6 that shows you how can we achieve a lazy load easily.
Figure 1.6
- EclipseLink capable to lazy load either ManyToMany and OneToMany without any need for weaving.
- The list of Phone entities does loaded lazily, cause it’s mapping marked as Lazy fetch type at the Employee entity.
- The list of Project entities does loaded lazily, cause it’s mapping marked as Lazy fetch type at the Employee entity.
- Each Lazy Loading mapping can be navigated by accessing the attribute for which the mapping belongs to.
Navigating ManyToMany and OneToMany Lazy Load
By executing the following code, you have a chance to navigate those lazily loaded mappings:
Employee employee = em.find(Employee.class, 1); System.out.println("Phones Size :: "+ employee.getPhones().size()); System.out.println("Projects Size :: "+ employee.getProjects().size());
- Once you’ve been accessing the attribute that does relate to the mapping the EclipseLink will fetch the dependent objects directly.
The next coming fragment of output shows you the results in of executing the last code:
Phones Size :: 1 Projects Size :: 2
Eager Loading
As we’ve mentioned previously, the Eager strategy is a requirement on the persistence provider runtime, in that the value must be eagerly fetched. Let’s look at the Employee entity in case the ManyToMany and OneToMany annotation have been changed to be fetched eagerly.
Employee.java
package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorValue; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="EmployeeType") @DiscriminatorValue(value="EMP") @EntityListeners(value={PersistenceContextListener.class}) public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @Embedded private EmployeePeriod employeePeriod; @OneToMany(mappedBy="employee", cascade=CascadeType.ALL,fetch=FetchType.EAGER) private List phones; // OneToMany Eager Load Association @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER) @JoinTable( name="employee_project", joinColumns={@JoinColumn(name="employeeId")}, inverseJoinColumns={ @JoinColumn(table="project",name="projectId"), @JoinColumn(table="localproject",name="projectId"), @JoinColumn(table="globalproject",name="projectId")} ) private List projects; // ManyToMany EAGER Load Association @OneToOne(cascade={CascadeType.ALL},fetch=FetchType.LAZY,targetEntity=Address.class) @JoinColumns({ @JoinColumn(name="AddressId",referencedColumnName="AddressId"), @JoinColumn(name="EmployeeAddressCountryId",referencedColumnName="AddressCountryId"), @JoinColumn(name="EmployeeAddressCityId",referencedColumnName="AddressCityId")} ) private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public List getPhones() { return phones; } public void setPhones(List phones) { this.phones = phones; } public List getProjects() { return projects; } public void setProjects(List projects) { this.projects = projects; } public EmployeePeriod getEmployeePeriod() { return employeePeriod; } public void setEmployeePeriod(EmployeePeriod employeePeriod) { this.employeePeriod = employeePeriod; } }
See Figure 1.7 that shows you the impact of using Eager.
Figure 1.7
- Each dependent objects are fetched eagerly without need for accessing the attributes.
- The Address is already fetched eagerly, cause we’ve removed the eclipselink weaving that could be used for enabling the OneToOne and ManyToOne lazy loading. Whenever such that Weaving is omitted, the Eclipselink persistence provider will load those assoications (OneToOne and ManyToOne) eagerly.
Using @Baisc To Provide Fetch Type
Typically, the using @Basic for providing a fetch type value is identical to what you’ve seen when it comes to provide such that value using one of the association annotations. But remember that @Basic couldn’t be used on a non-primitive data types (Also, other types is permitted to be used with the @Basic).
You can experienced using of @Basic on the employeeName and see the different if you’ve enabled the weaving. absolutely, you will get the same result as you did see.
Summary
The Lazy and Eager load are strategies considered by the Persistence Provider for optimizing the performance of the developed applications especially, those that could maintain a huge amount of objects and their associations. Lazy load strategy is a hint for persistence provider runtime to defer the fetching of associated objects until the accessing of the related attributes took place. At the other hand the Eager strategy, in that the associated objects are fetched directly. Java Persistence API provides you two different ways to determine the fetch type either @Basic or using of one of the association annotations (OneToOne, OneToMany, ManyToOne and ManyToMany).