Jump to: navigation, search

Talk:EclipseLink/Development/2.1/AdvancedJPA Queries/FetchGroup

Revision as of 13:11, 5 May 2010 by Andrei.ilitchev.oracle.com (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

FetchGroup Copy

  • Session already has a copyObject() API, this should just be exposed on the JpaEntityManager interface and allow an optional FetchGroup argument. I am not a fan of having static Helper objects, but using our public interface and the standard JPA 2.0 unwrap API.
  • I am OK with this. --Doug 15:08, 15 April 2010 (UTC)

FetchGroup "Sparse" Merge

  • The UnitOfWork merge() APIs should be exposed on our JpaEntityManager interface (shallowMerge, deepMerge, merge(Object, FetchGroup). I am not a fan of having static Helper objects, but using our public interface and the standard JPA 2.0 unwrap API.
  • But this does not change the default behaviour as proposed by Andrei which is to merge based on the attached FetchGroup and ignore those attributes that were not fetched.
  • My intention is that any merge api native or EntityManager would have the same behaviour it currently does but has the additional constraint that it is limited to the fetched attributes as defined by the FetchGroup atatched to the entity implementing FetchGroupTracker. --Doug 15:10, 15 April 2010 (UTC)

EntityFetchGroup

  • I don't see the reason for having an EntityFetchGroup. The Entity should just hold the FetchGroup from the query (which should be cached in the query). If the Entity needs to add something to the fetch-group it should first copy it, perhaps have a shared flag in the FetchGroup if required.
  • The purpose of the EntityFetchGroup is to address:
    1. Entity specific FetchGroup state separate from the shared FetchGroup so that setting un-fetched attributes augments the FetchGroup versus loading all of attributes as well as a means to handle merged/union of FetchGroups for overlapping reads.
    2. Serialization: The EntityFetchGroup could handle specialized serialization so that its detached version holds the complete set of fetched attributes
  • Using a single FetchGroup with lazy cloning for these cases is also possible.

--Doug 14:59, 15 April 2010 (UTC)

Refreshing

  • There should be some way to refresh a partial object and have the version updated. A normal query should union the two fetch groups and not refresh the version. A refresh query should not union the fetch groups but use the new one only, and refresh the version. Otherwise, there is no way to refresh a stale object without fetching the object in its entirety.

Serialization, instantiation

  • I do not think fetch groups should be coupled with serialization support. Serialization support is something that any user serializing requires, and should not require the usage of fetch groups. We should have a separate option on query to force instantiation of a set of attributes. Fetch groups and join fetching could have an option to automatically add their relationships to this separate option. The instantiation must be implemented very carefully to avoid cache deadlocks.
  • I believe we need a solution for 2.1 and at present this is what I have proposed. Users migrating to EclipseLink are looking for better solution for graph detachment (including serialization) with relationships. My preference is to add this to FetchGroup support versus adding another construct with overlapping functionality. --Doug 15:08, 15 April 2010 (UTC)
    • Note: at present the _persistence_FetchGroup attribute woven into an entity is transient. This means that when serialized the fact that the entity was partially loaded based on a FetchGroup is lost and any subsequent usage of the entity could result in data corruption.
  • I agree with Doug but in any case our FetchGroup support must be serialization aware. If a user is serializing a FetchGroup Entity to an environment where the same PU is available and has been deployed EclipseLink must correctly serialize the partially fetched Entity and throw exceptions when an unfetched attribute is accessed.

@FetchAttribute

  • Is the really required since it does not seem to do anything but have a name? Does it have any other attributes?

The current definition added to EclipseLink trunk (2.1) is:

@Target({TYPE})
@Retention(RUNTIME)
public @interface FetchGroup {
    /**
     * (Required) The fetch group name.
     */
    String name(); 
 
    /**
     * (Required) The list of attributes to fetch.
     */
    FetchAttribute[] attributes();
}

This additional annotation was defined to handle further qualification of the attribute.

Attempts to access unfetched attributes will cause an exception to be thrown

  • Our indirection support can load results within a detached object. Our FetchGroup support should be able to do the same.

The requirements I am getting is that unfetched attributes in detached/serialized objects be null and not a proxy load mechanism. --Doug 15:00, 15 April 2010 (UTC)

  • Let's not confuse detached with serialized. As of JPA they are two different concepts. An Entity may be detached from an EntityManager without having been serialized. Users like that we allow access to unloaded attributes after EntityManager detachment (just search the Hibernate forum). We should extend that same support to FetchGroupTrackers as well
  • I believe we are in agreement: --Doug 15:38, 15 April 2010 (UTC)
    • Entities that are detached from a PC but in the same memory space through standard JPA detachment semantics can load their unfetched attributes the same as we handle lazy relationships today.
    • Entities that are detached through JpaEntityManager.copy(Object, FetchGroup) will only have their specified attributes populated and all others will be null. Attempts to get these unfetched attributes will result in an exception.
    • Entities that are detached through serialization that have a FetchGroup attached will only have their specified attributes populated and all others will be null (or default value for primitives). Attempts to get these unfetched attributes will result in an exception.
  • Actually the above comes to me a bit as surprise. I would have expected FetchGroups to make additonal loading after detachment unnecessary since by means of FetchGroups an application declares what it relies upon to be loaded. --Kwesi.sap.com 09:49, 30 April 2010 (UTC)

FetchGroup and Relationship populating queries

  • At present a query executed to populate a relationship where there is no default FetchGroup on the target descriptor causes the query to be run with no FetchGroup and thus require all non-lazy mappings to be populated. I believe we need to treat these queries 'special' with respect to FetchGroup so that traversing a relationship that will not cause a partially loaded entity that would be the resulting target of the relationship to have its complete state loaded. --Doug 15:15, 15 April 2010 (UTC)

Nested Fetch Group on Queries and Descriptor; flat (non-nested) Fetch Groups on Entities

  • The is a functional difference between FetchGroup applied to a query and the FetchGroup held by an entity:
    • the former is a command (specifying the minimum set of attributes to be assigned to the object as the result of the query),
    • the latter is a state (describing the actual set of attributes that the object has).
  • James has suggested that nested fetch groups don't make sense for an object because the state of the related object is independent from the object's state.
    • Instead both the original and the related object should have "flat" (non-nested) fetch groups of their own.
  • The result of the query MUST contain AT LEAST the attributes specified in query's fecth group, but it may contain extra attributes.
    • Object read with a FetchedGroup is merged with the object in cache;
    • The resulting object's FetchGroup is a union of the two fetch groups.
      • If one of the objects doesn't have FecthGroup neither does the resulting object.
  • It would be probably more accurate to call FetchGroup applied to a query a FetchPlan - but to remain compatible with the existing code (that allows setting a FetchGroup on a Query) we may keep the original name FetchGroup. We could use EntityFetchGroup name (used in Doug's prototype) for FetchGroup held by entity.
  • Example: query for Employee using FetchGroup{"firstName", "lastName", "phones.areaCode"}:
  1. FetchGroup is extended to include the mandatory attributes: primary key and optimistic locking version;
    1. The resulting Employee object should have a EntityFetchGroup that contains al least the following attributes: {"id", "version", "firstName", "lastName", "phones"}.
      1. Note that it may end up containing some extra attributes, too.
  2. If query allowed to use cache (checkForEarlyReturn) then it searches for the Employee object in the cache
    1. If found use the object from the cache,
      1. If some of required attributes are missing from the cached object then read them from the db;
      2. EntityFetchGroup of the resulting object is a union of EntityFetchGroup of the cached object and EntityFetchGroup derived from query's FetchGroup.
        1. If the cached object doesn't have EntityFetchGroup then neither does the resulting object.
  3. The query that will read Phones assigned FetchGroup derived from the original query's FetchGroup: {"areaCode"}
  4. FetchGroup is extended to include the mandatory attributes: two dimensional primary key;
    1. The resulting Phone objects should have a EntityFetchGroup that contains at least the following attributes: {"owner", "type", "areaCode"}.
  5. query searches for Phone objects in the cache and merges with them and/or reads from the db so that the required attributes are fetched.

Future @FetchGroup

--Doug 13:38, 4 May 2010 (UTC)

Assuming that @FetchGroup works as documented in the wiki like:

@Entity
@FetchGroup(name="named-example", attributes={
        @FetchAttribute(name="id"), 
        @FetchAttribute(name="version"), 
        @FetchAttribute(name="firstName"), 
        @FetchAttribute(name="lastName"), 
        @FetchAttribute(name="address")
})
public class Employee{

We could then in the future extend this configuration as:

@Entity
@FetchGroup(name="named-example", 
    startWith=DEFAULT
    attributes={
        @FetchAttribute(name="id"), 
        @FetchAttribute(name="version"), 
        @FetchAttribute(name="address" startWith=DEFAULT), 
        @FetchAttribute(name="phoneNumbers" startWith=MINIMAL),
        @FetchAttribute(name="phoneNumbers.owner")
    },
    exclude={
        @FetchAttribute(name="salary"),
        @FetchAttribute(name="address.country")
    })
public class Employee{

Based on this above config a query using this FetchGroup would return (assuming address and phoneNumbers relationships are forced to be loaded):

Employee(id, version, firstName, lastName, gender, startTime, endTime, period, address(LAZY), phoneNumbers(LAZY), projects(LAZY), manager(LAZY), managedEmployees(LAZY)

Address(id, street, city, province, postalCode)

PhoneNumber(id, type, owner)

--Andrei.ilitchev.oracle.com 18:11, 5 May 2010 (UTC)

I would change exclude to an array of attribute names.

@Entity
@FetchGroup(name="named-example", 
    startWith=DEFAULT
    attributes={
        @FetchAttribute(name="id"), 
        @FetchAttribute(name="version"), 
        @FetchAttribute(name="address" startWith=DEFAULT), 
        @FetchAttribute(name="phoneNumbers" startWith=MINIMAL),
        @FetchAttribute(name="phoneNumbers.owner")
    },
    exclude={"salary", "address.country"}
)