Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Development/2.1/AdvancedJPA Queries/FetchGroup
EclipseLink 2.1: Enhanced FetchGroup Support
This feature will make major enhancements to EclipseLink's existing FetchGroup support to extends its use beyond lazy loading of basics to address:
- Usage with queries to define the complete depth of a query result that will be loaded
- Usage with copying/detaching entities to a given depth
- Usage with merging of partial entities
Related Bugs
- bug 244124: Add Nested FetchGroup support
Contents
Requirements
This feature will enhance FetchGroup to address the following requirements:
The enhancement to FetchGroup support will include addressing the following requirements:
Configuration
- Default FetchGroup will be defined when one or more basic mappings are configured LAZY and the entity class implements FetchGroupTracker (typically introduced through weaving).
- The default FetchGroup defined by mappings will not involve relationships
- The default FetchGroup can be customized through API
- Named FetchGroup can be defined using annotations, eclispelink-orm.xml, or API
- A FetchGroup is assumed to include all required attributes even if not specified. These required attributes will be added to the FetchGroup when it is initialzied
- Initialization of default and named FetchGroup occurs when the FetchGroup manager is initialized
- Dynamic FetchGroup are initialized when first used.
Query Usage
- The default FetchGroup is used on any query/find operation when no FetchGroup is specified
- A query/find can be customized to not use the default FetchGroup.
- A named FetchGroup can be specified that will be used if it exists
- Can be specified using JPA query hint or directly on the native query object
- If the named FetchGroup cannot be found in the descriptor hierarchy none will be used a warning message logged
Triggering Attempt to access an attribute that's not part of the fetch group triggers reading of the rest of the object:
- Outside transaction entire object (fetch group members included) is refreshed in the shared cache.
- Inside transaction the previously fetched attributes are NOT refreshed. That means that:
- Changes that were potentially made to the fetched attributes by the user won't be overridden.
- Optimistic lock exception will be thrown (on commit or flush) if the object changed in the data base since read originally (version field is always part of any fetch group).
Cached Entities
- If an entity is already cached (shared, isolated, UOW) then the resulting entity must have all of the items specified in the FetchGroup loaded.
- If the cached object has FetchGroup then resulting object will have a FetchGroup that is the union of the original one and the new one.
- That allows individual triggering of the attributes originally excluded from the fetch group (as opposed to all at once):
- Before accessing the not fetched attribute re-select the object with a new fetch group that contains this attribute only.
- That allows individual triggering of the attributes originally excluded from the fetch group (as opposed to all at once):
- When returning an entity that is partial the FetchGroup MUST always include all items that are populated
- If the cached object has FetchGroup then resulting object will have a FetchGroup that is the union of the original one and the new one.
Detaching Partial Entities
- Entities that are detached when they are partially populated based on a FetchGroup must maintain a FetchGroup in their state so that what has been loaded versus what has not been is known.
- Attempts to access unfetched attributes will cause an exception to be thrown
- Modifying the state of a partial entity will cause its detached FetchGroup to be enhanced to include additional attributes that are set
- in the case of collections the full collection must be set
- When a partial entity with a FetchGroup is merged into a persistence context only those items defined in the FetchGroup will be merged
- The cascade MERGE configurations on the mappings will be used and thus items in the FetchGroup without cascade MERGE configured will be ignored.
Usage Examples
The following usage examples are provided to assist in the understanding of this new functionality. The complete implementation details are cobvered in the design section of this specification.
Configuration
In order to use a FetchGroup developers must configure default, named, or dynamic FetchGroup instances for use in queries, copying, and merging of entities.
Default FetchGroup
The default FetchGroup is determined through the use of fetch=LAZY on basic mappings. There is no support for relationships in default the default FetchGroup unless the default FetchGroup is manually configured on an entity type's descriptor using API (DescritporCustomizer).
DescriptorCustomizer Example
FetchGroup phoneFG = new FetchGroup(); phoneFG.addAttribute("number"); ClassDescriptor phoneDescriptor = session.getClassDescriptor(PhoneNumber.class); phoneDescriptor.getFetchGroupManager().setDefaultFetchGroup(phoneFG);
Named FetchGroup
A named FetchGroup can be configured through annotations or API (DecsriptorCustomizer). This feature includes extensions to the @FetchGroup annotation and eclipselink-orm.xml to support defining FetchGroup items for relationships.
Simple Annotation Example
@Entity @FetchGroup(name="named-example", attributes={ @FetchAttribute(name="id"), @FetchAttribute(name="version"), @FetchAttribute(name="name"), }) public class MyEntity { @Id private int id; @Version private long version; private String name; private int size;
Relationships Annotation Example
@Entity @FetchGroup(name="named-example", attributes={ @FetchAttribute(name="id"), @FetchAttribute(name="version"), @FetchAttribute(name="firstName"), @FetchAttribute(name="lastName"), @FetchAttribute(name="address.city"), }) public class Employee{
EclipseLink ORM XML Example
Descriptor Customizer Example
Querying
A FetchGroup is used in the processing of a query when a default FetchGroup exists on the entity type's descriptor or one is specified on the query.
Named FetchGroup Example
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.id = :ID"); query.setParameter("ID", Queries.minimumEmployeeId(em)); query.setHint(QueryHints.FETCH_GROUP_NAME, "test");
Dynamic FetchGroup Example
Query query = em.createQuery("SELECT e FROM Employee e WHERE e.gender = :GENDER"); query.setParameter("GENDER", Gender.Male); // Define the fields to be fetched on Employee FetchGroup fg = new FetchGroup(); fg.addAttribute("id"); fg.addAttribute("version"); fg.addAttribute("firstName"); fg.addAttribute("lastName"); fg.addAttribute("address.city"); fg.addAttribute("address.postalCode"); // Configure the dynamic FetchGroup query.setHint(QueryHints.FETCH_GROUP, fg); List<Employee> emps = query.getResultList();
Detached Entities
The following usage examples illustrate how a FetchGroup can be used with detached entities.