Jump to: navigation, search

Difference between revisions of "EclipseLink/Examples/JPA/AttributeGroup"

(LoadGroup)
(Copy Examples)
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Starting with [[EclipseLink/Release/2.1.0|EclipseLink 2.1.0] EclipseLink has introduced the concept of an AttributeGroup that can be used configure the use of partial entities in fetch, load, copy, and merge operations. This example illustrates how this functionality can be used.
+
Starting in the [[EclipseLink/Release/2.1.0|EclipseLink 2.1.0]] release, EclipseLink has introduced the concept of an [http://www.eclipse.org/eclipselink/api/2.1/org/eclipse/persistence/queries/AttributeGroup.html AttributeGroup] that can be used configure the use of partial entities in fetch, load, copy, and merge operations. This example illustrates how this functionality can be used.
 +
:'''Fetch''': Control which attributes and their associated columns are retrieved from the database
 +
:'''Load''': Control which relationships in the entities returned from a query are populated
 +
:'''Copy''': Control which attributes are copied into a new entity instance
 +
:'''Merge''': Merge only those attributes fetched, loaded, or copied into an entity
  
 
== AttributeGroup Types and Operations ==
 
== AttributeGroup Types and Operations ==
Line 16: Line 20:
 
==== Full FetchGroup ====
 
==== Full FetchGroup ====
  
TODO
+
A FetchGroup when first created is assumed to be empty. The user must add the attributes to the FetchGroup. If a FetchGroup is required with all of the attributes then the [http://www.eclipse.org/eclipselink/api/2.1/org/eclipse/persistence/descriptors/FetchGroupManager.html#createFullFetchGroup%28%29 FetchGroupManager.createFullFetchGroup()] must be used.
  
 
==== Load/LoadAll with FetchGroup ====
 
==== Load/LoadAll with FetchGroup ====
Line 28: Line 32:
 
=== CopyGroup ===
 
=== CopyGroup ===
  
The CopyGroup replaces the deprecated [http://www.eclipse.org/eclipselink/api/2.0.1/org/eclipse/persistence/sessions/ObjectCopyingPolicy.html ObjectCopyPolicy] being used to define how a entity is copied. In addition to specifying the attributes defining what should be copied from the source entity graph into the target copy the CopyGroup also allows definition of:
+
The CopyGroup replaces the deprecated [http://www.eclipse.org/eclipselink/api/2.1/org/eclipse/persistence/sessions/ObjectCopyingPolicy.html ObjectCopyPolicy] being used to define how a entity is copied. In addition to specifying the attributes defining what should be copied from the source entity graph into the target copy the CopyGroup also allows definition of:
 
* shouldResetPrimaryKey: Reset the identifier attributes to their default value. This is used when the copy operation is intended to clone the entity in order to make a new entity with similar state to the source. Default is false.
 
* shouldResetPrimaryKey: Reset the identifier attributes to their default value. This is used when the copy operation is intended to clone the entity in order to make a new entity with similar state to the source. Default is false.
 
* shouldRestVersion: Reset the optimistic version locking attribute to its default value in the copies. Default is false.
 
* shouldRestVersion: Reset the optimistic version locking attribute to its default value in the copies. Default is false.
* depth: defines cascade mode for handling relationships. By default CASCADE_PRIVATE_PARTS is used but it can also be configure to NO_CASCADE, CASCADE_ALL_PARTS, and CASCADE_TREE (which should be used if the attribute group's specified items are all that is to be copied).
+
* depth: defines cascade mode for handling relationships. By default CASCADE_PRIVATE_PARTS is used but it can also be configured to NO_CASCADE and CASCADE_ALL_PARTS.
 +
** a new depth () not available in ObjectCopyPolicy) CASCADE_TREE is default in case the group has at least one attribute (addAttribute method has been called on the group).
 +
 
 +
There are significant differences between behaviours of CopyGroup with CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE  vs. CASCADE_TREE:
 +
* attributes that are not copied:
 +
** CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: shared between the copy and original;
 +
** CASCADE_TREE: not set in the copy;
 +
* shouldResetPrimaryKey:
 +
** CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: if true then the primary key of the copy is not set;
 +
** CASCADE_TREE: if false then the primary key attributes are always copied, if true then only primary key attribute(s) that are not specified in the group are not set (explicitly specified in the group primary key attributes are always copied);
 +
*** if set to true, then the copy object never assigned a fetch group;
 +
**** use this option to create a new object from original by persisting the copy (usually after setting some new attribute values);
 +
*** if set to false, then copy object has a fetch group if there is at least one non copied attribute.
 +
**** use this (default) option for sparse merge.
 +
* shouldResetVersion
 +
** CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: ignored;
 +
** CASCADE_TREE: if false then version attribute is always copied, if true then copied only if specified in the group (explicitly specified in the group version attribute is always copied);
  
 
=== Merging ===
 
=== Merging ===
Line 59: Line 79:
 
</fetch-group>
 
</fetch-group>
 
</source>
 
</source>
 +
 +
'''Using Named FetchGroup on Query'''
 +
<source lang="java">
 +
TypedQuery query = em.createQuery("SELECT e FROM Employee e", Employee.class);
 +
 +
query.setHint(QueryHints.FETCH_GROUP_NAME, "names");
 +
</source>
 +
 +
=== Dynamic FetchGroup ===
 +
 +
A FetchGroup can be created dynamically within the application's code and associated with a query using [http://www.eclipse.org/eclipselink/api/2.1/org/eclipse/persistence/config/QueryHints.html#FETCH_GROUP QueryHints.Fetch_Group].
 +
 +
<source lang="java">
 +
FetchGroup group = new FetchGroup();
 +
 +
group.addAttribute("firstName");
 +
group.addAttribute("lastName");
 +
 +
// Load the full PhoneNumber instances
 +
FetchGroup phoneGroup = em.unwrap(Server.class).getClassDescriptor(PhoneNumber.class).getFetchGroupManager().createFullFetchGroup();
 +
group.addAttribute("phoneNumbers", phoneGroup);
 +
 +
query.setHint(QueryHints.FETCH_GROUP, group);
 +
</source>
 +
 +
=== Load Examples ===
 +
 +
This example uses a load group to specify the graph to be retrieved. The BATCH hint is used to reduce the number of SELECT statements. The BATCH hint requires the JPQL alias as it is processed as part of the query execution. The LoadGroup is part opf post processing of the query result and therefore is not tied to JPQL and does not use the alias.
 +
 +
<source lang="java">
 +
TypedQuery<Division> q = em.createNamedQuery("Division.findByName", Division.class);
 +
q.setParameter("NAME", name);
 +
 +
// Specify that all teams and all team's players should be loaded
 +
LoadGroup lg = new LoadGroup();
 +
lg.addAttribute("teams.players");
 +
q.setHint(QueryHints.LOAD_GROUP, lg);
 +
 +
// Specify the use of batch loading to reduce the number of SQL statements
 +
q.setHint(QueryHints.BATCH, "d.teams.players");
 +
 +
return q.getSingleResult();
 +
</source>
 +
 +
The above query could also be accomplished using a FetchGroup configured for loading as well:
 +
 +
<source lang="java">
 +
TypedQuery<Division> q = em.createNamedQuery("Division.findByName", Division.class);
 +
q.setParameter("NAME", name);
 +
 +
// Specify that all teams and all team's players should be loaded
 +
FetchGroup fg = new FetchGroup();
 +
fg.addAttribute("teams.players");
 +
fg.setShouldLoadAll(true);
 +
q.setHint(QueryHints.FETCH_GROUP, fg);
 +
 +
return q.getSingleResult();
 +
</source>
 +
  
 
=== Copy Examples ===
 
=== Copy Examples ===
  
Here an Employee entity is copied with it basic names, address and phoneNumbers.
+
Here an Employee entity is copied with it basic names, address's city and street and phoneNumbers' area code and number
  
 
<source lang="java">
 
<source lang="java">
 
CopyGroup group = new CopyGroup();
 
CopyGroup group = new CopyGroup();
group.cascadeTree(); // Only copy the specified attribute group items
+
// Because the group has attributes CASCADE_ATTRIBUTES is used by default: only copy the specified attribute group items
 
group.addAttribute("firstName");
 
group.addAttribute("firstName");
 
group.addAttribute("lastName");
 
group.addAttribute("lastName");
group.addAttribute("address");
+
group.addAttribute("address.city");
group.addAttribute("phoneNumbers");
+
group.addAttribute("address.street");
 +
group.addAttribute("phoneNumbers.areaCode");
 +
group.addAttribute("phoneNumbers.number");
  
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
 
</source>
 
</source>
  
 +
If the reference attribute specified as a "leaf" (with no sub attributes) then all its non-reference attributes are copied (but primary key is copied, too - even if it's a reference attribute).
 +
Let's assume that Address class doesn't have any non-reference attributes - then
 +
<source lang="java">
 +
group.addAttribute("address");
 +
</source>
 +
is equivalent with adding all the address's attributes:
 +
<source lang="java">
 +
group.addAttribute("address.id");
 +
group.addAttribute("address.version");
 +
group.addAttribute("address.country");
 +
group.addAttribute("address.province");
 +
group.addAttribute("address.postalCode");
 +
group.addAttribute("address.city");
 +
group.addAttribute("address.street");
 +
</source>
 +
 +
Let's assume that PhoneNumber class has a composite primary key: {owner, type} - where owner is a reference to it's owner Employee and type is a string (that code have values like 'Home", "Work", "Cell etc).
 +
Just like in Address's case, adding phoneNumber attribute is equivalent to adding all PhoneNumber's attributes (the only reference attribute "owner" is also added because it's part of primary key).
 +
Because of that
 +
<source lang="java">
 +
group.addAttribute("phoneNumbers");
 +
</source>
 +
would trigger copying of the whole owner Employee - not just firstName and lastName we wanted to copy.
 +
To overcome that there is a way to specify that we don't need the whole owner:
 +
<source lang="java">
 +
group.addAttribute("phoneNumbers.owner.id");
 +
group.addAttribute("phoneNumbers.type");
 +
group.addAttribute("phoneNumbers.areaCode");
 +
group.addAttribute("phoneNumbers.number");
 +
</source>
 
==== Clone Entity using Copy and Persist ====
 
==== Clone Entity using Copy and Persist ====
  
Line 81: Line 192:
 
<source lang="java">
 
<source lang="java">
 
CopyGroup group = new CopyGroup();
 
CopyGroup group = new CopyGroup();
group.cascadePrivateParts(); // Copy all attributes and only private-owned relationships
+
// Because the group has no attributes CASCADE_PRIVATE_PARTS depth is used by default: copy all attributes and only private-owned relationships
 
group.setShouldResetPrimaryKey(true);
 
group.setShouldResetPrimaryKey(true);
group.setShouldResetVersion(true);
 
  
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
 
System.out.println(">>> Employee copied");
 
System.out.println(">>> Employee copied");
  
 +
// pk should be reset
 +
empCopy.setId(newId);
 +
// non-copied attributes are shared with the original - most (or all) of them usually have to be cleared and reset.
 +
empCopy.setAddress(newAddress);
 +
empCopy.setPhoneNumbers().clear();
 +
empCopy.addPhoneNumber(new PhoneNumber(...));
 +
empCopy.addPhoneNumber(new PhoneNumber(...));
 
// Persist the employee copy
 
// Persist the employee copy
 
em.persist(empCopy);
 
em.persist(empCopy);

Revision as of 06:24, 25 June 2012

Starting in the EclipseLink 2.1.0 release, EclipseLink has introduced the concept of an AttributeGroup that can be used configure the use of partial entities in fetch, load, copy, and merge operations. This example illustrates how this functionality can be used.

Fetch: Control which attributes and their associated columns are retrieved from the database
Load: Control which relationships in the entities returned from a query are populated
Copy: Control which attributes are copied into a new entity instance
Merge: Merge only those attributes fetched, loaded, or copied into an entity

AttributeGroup Types and Operations

FetchGroup

The FetchGroup which has existed in EclipseLInk for a number of releases defines which attributes should be fetched (selected from the database) when the entity is retrieved as the result of a query execution. The inclusion of relationship attributes in a FetchGroup only determines if the attribute's required columns should be fetched and populated. In the case of a lazy fetch type the inclusion of the attribute simply means that its proxy will be created to enable lazy loading when accessed. To force a relationship mapping to be populated when using a FetchGroup on a query the attribute must be included in the group and must either be FetchType.EAGER or it must be included in an associated LoadGroup on the query.

Default FetchGroup

FetchGroup also has the notion of named and default FetchGroup which are managed by the FetchGroupManager. A default FetchGroup is defined during metadata processing if one or more basic mappings are configured to be lazy and the entity class implements FetchGroupTracker (typically introduced through weaving). The default FetchGroup is used on all queries for this entity type where no explicit FetchGroup or named FetchGroup is configured.

Named FetchGroup

A Named FetchGroup can be defined for an entity using [TODO @FetchGroup] or within the [[EclipseLink/Examples/JPA/EclipseLink-ORM.XML|eclipselink-orm.xml]

Full FetchGroup

A FetchGroup when first created is assumed to be empty. The user must add the attributes to the FetchGroup. If a FetchGroup is required with all of the attributes then the FetchGroupManager.createFullFetchGroup() must be used.

Load/LoadAll with FetchGroup

A FetchGroup can also be configured to perform a load operation of relationship mappings and nested relationship mappings.

LoadGroup

A LoadGroup is used to force a specified set of relationship attributes to be populated in a query result.

CopyGroup

The CopyGroup replaces the deprecated ObjectCopyPolicy being used to define how a entity is copied. In addition to specifying the attributes defining what should be copied from the source entity graph into the target copy the CopyGroup also allows definition of:

  • shouldResetPrimaryKey: Reset the identifier attributes to their default value. This is used when the copy operation is intended to clone the entity in order to make a new entity with similar state to the source. Default is false.
  • shouldRestVersion: Reset the optimistic version locking attribute to its default value in the copies. Default is false.
  • depth: defines cascade mode for handling relationships. By default CASCADE_PRIVATE_PARTS is used but it can also be configured to NO_CASCADE and CASCADE_ALL_PARTS.
    • a new depth () not available in ObjectCopyPolicy) CASCADE_TREE is default in case the group has at least one attribute (addAttribute method has been called on the group).

There are significant differences between behaviours of CopyGroup with CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE vs. CASCADE_TREE:

  • attributes that are not copied:
    • CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: shared between the copy and original;
    • CASCADE_TREE: not set in the copy;
  • shouldResetPrimaryKey:
    • CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: if true then the primary key of the copy is not set;
    • CASCADE_TREE: if false then the primary key attributes are always copied, if true then only primary key attribute(s) that are not specified in the group are not set (explicitly specified in the group primary key attributes are always copied);
      • if set to true, then the copy object never assigned a fetch group;
        • use this option to create a new object from original by persisting the copy (usually after setting some new attribute values);
      • if set to false, then copy object has a fetch group if there is at least one non copied attribute.
        • use this (default) option for sparse merge.
  • shouldResetVersion
    • CASCADE_PRIVATE_PARTS / CASCADE_ALL_PARTS / NO_CASCADE: ignored;
    • CASCADE_TREE: if false then version attribute is always copied, if true then copied only if specified in the group (explicitly specified in the group version attribute is always copied);

Merging

When a partial entity is merged into a persistence context that has an AttributeGroup associated with it defining which attributes are available only those attributes are merged. The relationship mappings within the entity are still merged according to their cascade merge settings.

Usage Examples

FetchGroup Examples

Named FetchGroup

Configuring using @FetchGroup

@FetchGroup(name="names", attributes={
        @FetchAttribute(name="firstName"), 
        @FetchAttribute(name="lastName")})

Configuring within eclipsleink-orm.xml

<entity class="model.Employee">
	<secondary-table name="SALARY" />
	<fetch-group name="names">
		<attribute name="firstName" />
		<attribute name="lastName" />
	</fetch-group>

Using Named FetchGroup on Query

TypedQuery query = em.createQuery("SELECT e FROM Employee e", Employee.class);
 
query.setHint(QueryHints.FETCH_GROUP_NAME, "names");

Dynamic FetchGroup

A FetchGroup can be created dynamically within the application's code and associated with a query using QueryHints.Fetch_Group.

FetchGroup group = new FetchGroup();
 
group.addAttribute("firstName");
group.addAttribute("lastName");
 
// Load the full PhoneNumber instances
FetchGroup phoneGroup = em.unwrap(Server.class).getClassDescriptor(PhoneNumber.class).getFetchGroupManager().createFullFetchGroup();
group.addAttribute("phoneNumbers", phoneGroup);
 
query.setHint(QueryHints.FETCH_GROUP, group);

Load Examples

This example uses a load group to specify the graph to be retrieved. The BATCH hint is used to reduce the number of SELECT statements. The BATCH hint requires the JPQL alias as it is processed as part of the query execution. The LoadGroup is part opf post processing of the query result and therefore is not tied to JPQL and does not use the alias.

TypedQuery<Division> q = em.createNamedQuery("Division.findByName", Division.class);
q.setParameter("NAME", name);
 
// Specify that all teams and all team's players should be loaded
LoadGroup lg = new LoadGroup();
lg.addAttribute("teams.players");
q.setHint(QueryHints.LOAD_GROUP, lg);
 
// Specify the use of batch loading to reduce the number of SQL statements
q.setHint(QueryHints.BATCH, "d.teams.players");
 
return q.getSingleResult();

The above query could also be accomplished using a FetchGroup configured for loading as well:

TypedQuery<Division> q = em.createNamedQuery("Division.findByName", Division.class);
q.setParameter("NAME", name);
 
// Specify that all teams and all team's players should be loaded
FetchGroup fg = new FetchGroup();
fg.addAttribute("teams.players");
fg.setShouldLoadAll(true);
q.setHint(QueryHints.FETCH_GROUP, fg);
 
return q.getSingleResult();


Copy Examples

Here an Employee entity is copied with it basic names, address's city and street and phoneNumbers' area code and number

CopyGroup group = new CopyGroup();
// Because the group has attributes CASCADE_ATTRIBUTES is used by default: only copy the specified attribute group items
group.addAttribute("firstName");
group.addAttribute("lastName");
group.addAttribute("address.city");
group.addAttribute("address.street");
group.addAttribute("phoneNumbers.areaCode");
group.addAttribute("phoneNumbers.number");
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);

If the reference attribute specified as a "leaf" (with no sub attributes) then all its non-reference attributes are copied (but primary key is copied, too - even if it's a reference attribute). Let's assume that Address class doesn't have any non-reference attributes - then

group.addAttribute("address");

is equivalent with adding all the address's attributes:

group.addAttribute("address.id");
group.addAttribute("address.version");
group.addAttribute("address.country");
group.addAttribute("address.province");
group.addAttribute("address.postalCode");
group.addAttribute("address.city");
group.addAttribute("address.street");

Let's assume that PhoneNumber class has a composite primary key: {owner, type} - where owner is a reference to it's owner Employee and type is a string (that code have values like 'Home", "Work", "Cell etc). Just like in Address's case, adding phoneNumber attribute is equivalent to adding all PhoneNumber's attributes (the only reference attribute "owner" is also added because it's part of primary key). Because of that

group.addAttribute("phoneNumbers");

would trigger copying of the whole owner Employee - not just firstName and lastName we wanted to copy. To overcome that there is a way to specify that we don't need the whole owner:

group.addAttribute("phoneNumbers.owner.id");
group.addAttribute("phoneNumbers.type");
group.addAttribute("phoneNumbers.areaCode");
group.addAttribute("phoneNumbers.number");

Clone Entity using Copy and Persist

In this example an employee entity is cloned using a CopyGroup to copy all attributes with the exception of relationships (1:1, 1:M, M:M) that are not configured as private-owned. The resulting copy of the entity is then persisted to have new identity but the same state as its source.

CopyGroup group = new CopyGroup();
// Because the group has no attributes CASCADE_PRIVATE_PARTS depth is used by default: copy all attributes and only private-owned relationships
group.setShouldResetPrimaryKey(true);
 
Employee empCopy = (Employee) em.unwrap(JpaEntityManager.class).copy(emp, group);
System.out.println(">>> Employee copied");
 
// pk should be reset
empCopy.setId(newId);
// non-copied attributes are shared with the original - most (or all) of them usually have to be cleared and reset.
empCopy.setAddress(newAddress);
empCopy.setPhoneNumbers().clear();
empCopy.addPhoneNumber(new PhoneNumber(...));
empCopy.addPhoneNumber(new PhoneNumber(...));
// Persist the employee copy
em.persist(empCopy);