Jump to: navigation, search

Difference between revisions of "Using Basic Unit of Work API (ELUG)"

m (Associating a New Source to an Existing Target Object)
m
 
(13 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
[[Image:Elug draft icon.png]] '''For the latest EclipseLink documentation, please see http://www.eclipse.org/eclipselink/documentation/ '''
 +
 +
----
 
<div style="float:right;border:1px solid #000000;padding:5px">__TOC__
 
<div style="float:right;border:1px solid #000000;padding:5px">__TOC__
 
[[Special:Whatlinkshere/Using Basic Unit of Work API (ELUG)|Related Topics]]</div>
 
[[Special:Whatlinkshere/Using Basic Unit of Work API (ELUG)|Related Topics]]</div>
Line 20: Line 23:
 
A unit of work remains valid after the <tt>commitAndResume</tt> method is called, as described in [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Resuming a Unit of Work After Commit]].
 
A unit of work remains valid after the <tt>commitAndResume</tt> method is called, as described in [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Resuming a Unit of Work After Commit]].
  
When using a unit of work with JTA, you can also use the advanced API <tt>getActiveUnitOfWork</tt> method, as described in [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Integrating the Unit of Work with an External Transaction Service]].
+
When using a unit of work with JTA, you should also use the advanced API <tt>getActiveUnitOfWork</tt> method, as described in [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Integrating the Unit of Work with an External Transaction Service]].
  
 
==Creating an Object==
 
==Creating an Object==
Line 39: Line 42:
 
  petClone.setType("Cat");
 
  petClone.setType("Cat");
 
  uow.commit();
 
  uow.commit();
 
 
  
 
This example shows a common alternative.
 
This example shows a common alternative.
Line 53: Line 54:
 
  uow.registerObject(pet);  
 
  uow.registerObject(pet);  
 
  uow.commit();
 
  uow.commit();
 
  
 
Both approaches produce the following SQL:
 
Both approaches produce the following SQL:
Line 59: Line 59:
  
 
You should follow the [[#Example 110-1|Creating an Object: Preferred Method]] example: it gets you into the pattern of working with clones and provides the most flexibility for future code changes. Working with combinations of new objects and clones can lead to confusion and unwanted results.
 
You should follow the [[#Example 110-1|Creating an Object: Preferred Method]] example: it gets you into the pattern of working with clones and provides the most flexibility for future code changes. Working with combinations of new objects and clones can lead to confusion and unwanted results.
 +
 +
  
 
==Modifying an Object==
 
==Modifying an Object==
  
In [[#Example 110-3|Modifying an Object]] example, a <tt>Pet</tt> is read prior to a unit of work: the variable <tt>pet</tt> is the cache copy clone for that <tt>Pet</tt>. Inside the unit of work, register the cache copy to get a working copy clone, then modify the working copy clone and commit the unit of work.
+
In the [[#Example 110-3|Modifying an Object]] example, a <tt>Pet</tt> is read prior to a unit of work: the variable <tt>pet</tt> is the cache copy clone for that <tt>Pet</tt>. Inside the unit of work, register the cache copy to get a working copy clone, then modify the working copy clone and commit the unit of work.
  
  
Line 76: Line 78:
 
[[#Example 110-4|Modifying an Object: Skipping the Registration Step]] example shows how to take advantage of the fact that you can query through a unit of work and get back clones, saving the registration step. However, the drawback is that you do not have a handle to the cache copy clone.
 
[[#Example 110-4|Modifying an Object: Skipping the Registration Step]] example shows how to take advantage of the fact that you can query through a unit of work and get back clones, saving the registration step. However, the drawback is that you do not have a handle to the cache copy clone.
  
If you wanted to do something with the updated <tt>Pet</tt> after the commit transaction, you would have to query the session to get it (remember that after a unit of work is committed, its clones are invalid and must not be used).
+
If you wanted to do something with the updated <tt>Pet</tt> after the commit transaction, you would have to query the session to get it (remember that after a unit of work is committed, its clones are invalid and should not be used).
  
  
Line 102: Line 104:
 
Deciding which approach to use depends on whether or not your code requires a reference to the cache copy clone of the new object after the unit of work is committed, and on how adaptable to change you want your code to be.
 
Deciding which approach to use depends on whether or not your code requires a reference to the cache copy clone of the new object after the unit of work is committed, and on how adaptable to change you want your code to be.
  
<br />
 
  
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
Line 109: Line 110:
 
|}
 
|}
  
<br />
 
  
 
===How to Associate a New Target to an Existing Source Object in a Unidirectional Relationship: Reference to the New Cache Object After Commit not Required===
 
===How to Associate a New Target to an Existing Source Object in a Unidirectional Relationship: Reference to the New Cache Object After Commit not Required===
Line 135: Line 135:
 
   
 
   
 
  petClone.setPetOwner(petOwner);
 
  petClone.setPetOwner(petOwner);
  petClone.getVetVisits().addElement(vetVisit);  
+
  petClone.getVetVisits().add(vetVisit);  
 
  uow.commit();
 
  uow.commit();
  
Line 153: Line 153:
  
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 +
  
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
Line 197: Line 198:
  
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 +
  
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
Line 202: Line 204:
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
|}
 
|}
 +
  
 
===How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Before Commit not Required===
 
===How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Before Commit not Required===
Line 228: Line 231:
 
   
 
   
 
  }
 
  }
 
  
 
[[#Example 110-8|Resolving Issues When Adding New Objects]] example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.
 
[[#Example 110-8|Resolving Issues When Adding New Objects]] example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.
Line 277: Line 279:
 
  '''// Commit the unit of work'''
 
  '''// Commit the unit of work'''
 
  uow.commit();
 
  uow.commit();
 
  
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
Line 286: Line 287:
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
|}
 
|}
 +
  
 
===How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Object Before Commit Required===
 
===How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Object Before Commit Required===
Line 315: Line 317:
 
   
 
   
 
  }
 
  }
 
  
 
[[#Example 110-10|Resolving Issues When Adding New Objects]] example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.
 
[[#Example 110-10|Resolving Issues When Adding New Objects]] example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.
Line 363: Line 364:
 
  uow.commit();
 
  uow.commit();
 
   
 
   
 
 
Because <tt>Employee</tt> method <tt>setManager</tt> modifies the <tt>Employee</tt> you pass in (as [[#Example 110-10|Resolving Issues When Adding New Objects]] example), you must pass in <tt>managerClone</tt> that <tt>registerObject</tt> returns.
 
Because <tt>Employee</tt> method <tt>setManager</tt> modifies the <tt>Employee</tt> you pass in (as [[#Example 110-10|Resolving Issues When Adding New Objects]] example), you must pass in <tt>managerClone</tt> that <tt>registerObject</tt> returns.
  
Line 375: Line 375:
  
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 
For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|Registering and Unregistering Objects]]).
 +
  
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
Line 380: Line 381:
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
'''Note:''' You cannot use <tt>UnitOfWork</tt> methods <tt>registerObject</tt>, <tt>registerNewObject</tt>, or <tt>registerExistingObject</tt> with an aggregate object (see [[Creating%20a%20Relational%20Descriptor%20(ELUG)|Creating Relational Aggregate Descriptors]]). Doing so will raise a ValidationException or other errors at commit time. For more information, see [[Using%20Advanced%20Unit%20of%20Work%20API%20(ELUG)|How to Work with Aggregates]].
 
|}
 
|}
 +
 +
  
 
==Associating a New Source to an Existing Target Object==
 
==Associating a New Source to an Existing Target Object==
Line 385: Line 388:
 
This section describes how to associate a new source object with an existing target object with one-to-many and one-to-one relationships.
 
This section describes how to associate a new source object with an existing target object with one-to-many and one-to-one relationships.
  
EclipseLink follows all relationships of all registered objects (deeply) in a unit of work to calculate what is new and what has changed. This is known as ''persistence by reachablity''. In [[#Associating a New Target to an Existing Source Object|Associating a New Target to an Existing Source Object]] example, we saw that when you associate a new target with an existing source, you can choose to register the object or not. If you do not register the new object, it is still reachable from the source object (which is a clone, hence it is registered). However, when you need to associate a new source object with an existing target, you must register the new object. If you do not register the new object, then it is not reachable in the unit of work, and EclipseLink will not write it to the database.
+
EclipseLink follows all relationships of all registered objects (deeply) in a unit of work to calculate what is new and what has changed. This is known as ''persistence by reachablity''. In [[#Associating a New Target to an Existing Source Object|Associating a New Target to an Existing Source Object]] example, you saw that when you associate a new target with an existing source, you can choose to register the object or not. If you do not register the new object, it is still reachable from the source object (which is a clone, hence it is registered). However, when you need to associate a new source object with an existing target, you must register the new object. If you do not register the new object, then it is not reachable in the unit of work, and EclipseLink will not write it to the database.
  
 
[[#Example 110-11|Associating a New Source to an Existing Target Object]] example shows how to create a new <tt>Pet</tt> and associate it with an existing <tt>PetOwner</tt>.
 
[[#Example 110-11|Associating a New Source to an Existing Target Object]] example shows how to create a new <tt>Pet</tt> and associate it with an existing <tt>PetOwner</tt>.
Line 403: Line 406:
 
  newPetClone.setPetOwner(existingPetOwnerClone);
 
  newPetClone.setPetOwner(existingPetOwnerClone);
 
  uow.commit();
 
  uow.commit();
 
 
  
 
This generates the following proper SQL:
 
This generates the following proper SQL:
Line 411: Line 412:
 
In this situation, you should register the new object and work with the working copy of the new object. If you associate the new object with the <tt>PetOwner</tt> clone without registering, it will not be written to the database.
 
In this situation, you should register the new object and work with the working copy of the new object. If you associate the new object with the <tt>PetOwner</tt> clone without registering, it will not be written to the database.
  
<br />
 
  
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
 
{| class="Note oac_no_warn" width="80%" border="1" frame="hsides" rules="groups" cellpadding="3" frame="hsides" rules="groups"
Line 418: Line 418:
 
|}
 
|}
  
<br />
 
  
 
If you fail to register the clone and accidentally associate the cache version of the existing object with the new object, then EclipseLink will generate an error which states that you have associated the cache version of an object ("from a parent session") with a clone from this unit of work. You must work with working copies in units of work.
 
If you fail to register the clone and accidentally associate the cache version of the existing object with the new object, then EclipseLink will generate an error which states that you have associated the cache version of an object ("from a parent session") with a clone from this unit of work. You must work with working copies in units of work.
Line 426: Line 425:
 
* [[#Associating a New Target to an Existing Source Object|Associating a New Target to an Existing Source Object]]
 
* [[#Associating a New Target to an Existing Source Object|Associating a New Target to an Existing Source Object]]
 
* [[#Associating an Existing Source to an Existing Target Object|Associating an Existing Source to an Existing Target Object]]
 
* [[#Associating an Existing Source to an Existing Target Object|Associating an Existing Source to an Existing Target Object]]
 +
 +
  
 
==Associating an Existing Source to an Existing Target Object==
 
==Associating an Existing Source to an Existing Target Object==
Line 431: Line 432:
 
This section explains how to associate an existing source object with an existing target object with one-to-many and one-to-one relationships.
 
This section explains how to associate an existing source object with an existing target object with one-to-many and one-to-one relationships.
  
As shown in [[#Example 110-12|Associating an Existing Source to Existing Target Object]], associating existing objects with each other in a unit of work is as simple as associating objects in Java. Just remember to only work with working copies of the objects.
+
As shown in [[#Example 110-12|Associating an Existing Source to Existing Target Object]] example, associating existing objects with each other in a unit of work is as simple as associating objects in Java. Just remember to only work with working copies of the objects.
  
  
Line 439: Line 440:
 
  UnitOfWork uow = session.acquireUnitOfWork();
 
  UnitOfWork uow = session.acquireUnitOfWork();
 
  Pet existingPetClone = (Pet)uow.readObject(Pet.class);
 
  Pet existingPetClone = (Pet)uow.readObject(Pet.class);
  Vector allVetVisitClones;
+
  List allVetVisitClones;
  allVetVisitClones = (Vector)uow.readAllObjects(VetVisit.class);
+
  allVetVisitClones = uow.readAllObjects(VetVisit.class);
  Enumeration enum = allVetVisitClones.elements();
+
  Iterator iter = allVetVisitClones.elements();
  while(enum.hasMoreElements()) {
+
  while(iter.hasNext()) {
     VetVisit vetVisitClone =(VetVisit)enum.nextElement();
+
     VetVisit vetVisitClone =(VetVisit)iter.next();
     existingPetClone.getVetVisits().addElement(vetVisitClone);
+
     existingPetClone.getVetVisits().add(vetVisitClone);
 
     vetVisitClone.setPet(existingPetClone);
 
     vetVisitClone.setPet(existingPetClone);
 
  };
 
  };
 
  uow.commit();
 
  uow.commit();
 
 
  
 
The most common error when associating existing objects is failing to work with the working copies. If you accidentally associate a cache version of an object with a working copy you will get an error at commit time indicating that you associated an object from a parent session (the cache version) with a clone from this unit of work.
 
The most common error when associating existing objects is failing to work with the working copies. If you accidentally associate a cache version of an object with a working copy you will get an error at commit time indicating that you associated an object from a parent session (the cache version) with a clone from this unit of work.
  
[[#Example 110-13|Associating Existing Objects]] shows another example of associating an existing source to an existing target object.
+
[[#Example 110-13|Associating Existing Objects]] example shows another example of associating an existing source to an existing target object.
  
  
Line 466: Line 465:
 
  Project project = (Project) uow.readObject(Project.class);
 
  Project project = (Project) uow.readObject(Project.class);
 
   
 
   
  '''/* When associating an existing object (read from the session) with a clone, we must make sure we register the existing object and assign its clone into a unit of work */'''
+
  '''// When associating an existing object (read from the session) with a clone,'''
 +
'''// make sure you register the existing object and assign its clone into a unit of work'''
 
   
 
   
  '''/* INCORRECT: Cannot associate an existing object with a unit of work clone. A QueryException will be thrown */'''
+
  '''// INCORRECT: Cannot associate an existing object with a unit of work clone.'''
 +
'''// A QueryException will be thrown'''
 
  '''//project.setTeamLeader(employee);'''
 
  '''//project.setTeamLeader(employee);'''
 
   
 
   
  '''/* CORRECT: Instead register the existing object then associate the clone */'''
+
  '''// CORRECT: Instead register the existing object then associate the clone'''
 
  Employee employeeClone = (Employee)uow.registerObject(employee);
 
  Employee employeeClone = (Employee)uow.registerObject(employee);
 
  project.setTeamLeader(employeeClone);
 
  project.setTeamLeader(employeeClone);
 
  uow.commit();
 
  uow.commit();
 
 
  
 
For more information, see the following:
 
For more information, see the following:
* [[#Associating a New Target to an Existing Source Object]]
+
* [[#Associating a New Target to an Existing Source Object|Associating a New Target to an Existing Source Object]]
* [[#Associating a New Source to an Existing Target Object]]
+
* [[#Associating a New Source to an Existing Target Object|Associating a New Target to an Existing Source Object]]
  
 
==Deleting Objects==
 
==Deleting Objects==
Line 495: Line 494:
 
* [[#How to Explicitly Delete from the Database|How to Explicitly Delete from the Database]]
 
* [[#How to Explicitly Delete from the Database|How to Explicitly Delete from the Database]]
 
* [[#What You May Need to Know About the Order in which Objects Are Deleted|What You May Need to Know About the Order in which Objects Are Deleted]]
 
* [[#What You May Need to Know About the Order in which Objects Are Deleted|What You May Need to Know About the Order in which Objects Are Deleted]]
 
  
  
Line 502: Line 500:
 
Relational databases do not have garbage collection like a Java Virtual Machine (JVM) does. To delete an object in Java you just remove the reference to the object. To delete a row in a relational database, you must explicitly delete it. Rather than tediously manage when to delete data in the relational database, use the mapping attribute <tt>privateOwnedRelationship</tt> to have EclipseLink manage the garbage collection in the relational database for you.
 
Relational databases do not have garbage collection like a Java Virtual Machine (JVM) does. To delete an object in Java you just remove the reference to the object. To delete a row in a relational database, you must explicitly delete it. Rather than tediously manage when to delete data in the relational database, use the mapping attribute <tt>privateOwnedRelationship</tt> to have EclipseLink manage the garbage collection in the relational database for you.
  
As shown in [[#Example 110-14| Specifying a Mapping as Privately Owned]], when you create a mapping using Java, use its <tt>privateOwnedRelationship</tt> method to tell EclipseLink that the referenced object is privately owned: that is, the referenced child object cannot exist without the parent object.
+
As shown in the [[#Example 110-14| Specifying a Mapping as Privately Owned]] example, when you create a mapping using Java, use its <tt>privateOwnedRelationship</tt> method to tell EclipseLink that the referenced object is privately owned: that is, the referenced child object cannot exist without the parent object.
  
  
Line 518: Line 516:
  
 
When you tell EclipseLink that a relationship is privately owned, you are specifying the following:
 
When you tell EclipseLink that a relationship is privately owned, you are specifying the following:
 
 
  
 
* If the source of a privately owned relationship is deleted, then delete the target.
 
* If the source of a privately owned relationship is deleted, then delete the target.
Line 528: Line 524:
 
The exception to this rule is the case when you have a many-to-many relationship in which a relation object is mapped to a relation table and is referenced through a one-to-many relationship by both the source and the target. In this case, if the one-to-many mapping is configured as privately owned, then when you delete the source, all the association objects will be deleted.
 
The exception to this rule is the case when you have a many-to-many relationship in which a relation object is mapped to a relation table and is referenced through a one-to-many relationship by both the source and the target. In this case, if the one-to-many mapping is configured as privately owned, then when you delete the source, all the association objects will be deleted.
  
Consider [[#Example 110-15|Privately Owned Relationships]].
+
Consider the [[#Example 110-15|Privately Owned Relationships]] example.
 
+
  
 
<span id="Example 110-15"></span>
 
<span id="Example 110-15"></span>
Line 540: Line 535:
 
  Pet petClone = (Pet)uow.readObject(Pet.class);
 
  Pet petClone = (Pet)uow.readObject(Pet.class);
 
  petClone.setPetOwner(null);
 
  petClone.setPetOwner(null);
  VetVisit vvClone =
+
  VetVisit vvClone = (VetVisit)petClone.getVetVisits().get(0);
        (VetVisit)petClone.getVetVisits().firstElement();
+
 
  vvClone.setPet(null);
 
  vvClone.setPet(null);
  petClone.getVetVisits().removeElement(vvClone);
+
  petClone.getVetVisits().remove(vvClone);
 
  uow.commit();
 
  uow.commit();
  
Line 558: Line 552:
 
===How to Explicitly Delete from the Database===
 
===How to Explicitly Delete from the Database===
  
If there are cases where you have objects that will not be garbage collected through privately owned relationships (especially root objects in your object model), then you can explicitly tell EclipseLink to delete the row representing the object using the <tt>deleteObject</tt> API, as shown in [[#Example 110-16|Explicitly Deleting]].
+
If there are cases where you have objects that will not be garbage collected through privately owned relationships (especially root objects in your object model), then you can explicitly tell EclipseLink to delete the row representing the object using the <tt>deleteObject</tt> API, as shown in the [[#Example 110-16|Explicitly Deleting]] example.
  
  
Line 570: Line 564:
 
The preceding code generates the following SQL:
 
The preceding code generates the following SQL:
 
  DELETE FROM PET WHERE (ID = 100)
 
  DELETE FROM PET WHERE (ID = 100)
 +
  
 
===What You May Need to Know About the Order in which Objects Are Deleted===
 
===What You May Need to Know About the Order in which Objects Are Deleted===
Line 585: Line 580:
  
 
[[Category: EclipseLink User's Guide]]
 
[[Category: EclipseLink User's Guide]]
[[Category: Draft]]
+
[[Category: Release 1]]
 
[[Category: Task]]
 
[[Category: Task]]

Latest revision as of 11:16, 23 July 2012

Elug draft icon.png For the latest EclipseLink documentation, please see http://www.eclipse.org/eclipselink/documentation/


Related Topics

This section explains the essential unit of work API calls that you are most likely to use throughout the development cycle. For more information, see Using Advanced Unit of Work API.


Acquiring a Unit of Work

This example shows how to acquire a unit of work from a client session object.

Server server = (Server) SessionManager.getManager().getSession(
                sessionName, MyServerSession.class.getClassLoader());
Session session = (Session) server.acquireClientSession();
UnitOfWork uow = session.acquireUnitOfWork();

You can acquire a unit of work from any session type. For more information about acquiring sessions at run time, see Acquiring a Session at Run Time with the Session Manager.

Note that you do not need to create a new session and log in before every transaction. The recommended pattern is to acquire a client session per client access (or thread), and then acquire the necessary unit of work from this client session.

The unit of work is valid until the commit or release method is called. After a commit or release transaction, a unit of work is not valid even if the transaction fails and is rolled back.

A unit of work remains valid after the commitAndResume method is called, as described in Resuming a Unit of Work After Commit.

When using a unit of work with JTA, you should also use the advanced API getActiveUnitOfWork method, as described in Integrating the Unit of Work with an External Transaction Service.

Creating an Object

When you create new objects in the unit of work, use the registerObject method to ensure that the unit of work writes the objects to the database at commit time.

The unit of work calculates commit order using foreign key information from one-to-one and one-to-many mappings. If you encounter constraint problems during a commit transaction, verify your mapping definitions. The order in which you register objects with the registerObject method does not affect the commit order.

Creating an Object: Preferred Method and Creating an Object: Alternative Method examples show how to create and persist a simple object (without relationships) using the clone returned by the unit of work registerObject method.


Creating an Object: Preferred Method

UnitOfWork uow = session.acquireUnitOfWork();
Pet pet = new Pet();
Pet petClone = (Pet)uow.registerObject(pet);
petClone.setId(100);
petClone.setName("Fluffy");
petClone.setType("Cat");
uow.commit();

This example shows a common alternative.

Creating an Object: Alternative Method

UnitOfWork uow = session.acquireUnitOfWork();
Pet pet = new Pet();
pet.setId(100);
pet.setName("Fluffy");
pet.setType("Cat");
uow.registerObject(pet); 
uow.commit();

Both approaches produce the following SQL:

INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (100, 'Fluffy', 'Cat', NULL)

You should follow the Creating an Object: Preferred Method example: it gets you into the pattern of working with clones and provides the most flexibility for future code changes. Working with combinations of new objects and clones can lead to confusion and unwanted results.


Modifying an Object

In the Modifying an Object example, a Pet is read prior to a unit of work: the variable pet is the cache copy clone for that Pet. Inside the unit of work, register the cache copy to get a working copy clone, then modify the working copy clone and commit the unit of work.


Modifying an Object

// Read in any pet
Pet pet = (Pet)session.readObject(Pet.class);
UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet) uow.registerObject(pet);
petClone.setName("Furry");
uow.commit();

Modifying an Object: Skipping the Registration Step example shows how to take advantage of the fact that you can query through a unit of work and get back clones, saving the registration step. However, the drawback is that you do not have a handle to the cache copy clone.

If you wanted to do something with the updated Pet after the commit transaction, you would have to query the session to get it (remember that after a unit of work is committed, its clones are invalid and should not be used).


Modifying an Object: Skipping the Registration Step

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet) uow.readObject(Pet.class);
petClone.setName("Furry");
uow.commit();

Both approaches produce the following SQL:

UPDATE PET SET NAME = 'Furry' WHERE (ID = 100)

Take care when querying through a unit of work. All objects read in the query are registered in the unit of work and therefore will be checked for changes at commit time. Rather than do a ReadAllQuery through a unit of work, it is better for performance to design your application to do the ReadAllQuery through a session, and then register in a unit of work only the objects that need to be changed.

Associating a New Target to an Existing Source Object

Consider the following options:

Deciding which approach to use depends on whether or not your code requires a reference to the cache copy clone of the new object after the unit of work is committed, and on how adaptable to change you want your code to be.


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.


How to Associate a New Target to an Existing Source Object in a Unidirectional Relationship: Reference to the New Cache Object After Commit not Required

Associating Without Reference to the Cache Object example shows how to associate a new target with an existing source in a unidirectional relationship without retaining a reference to the cache object.

When the Pet object is read using the unit of work, EclipseLink automatically registers it. Because there is a unidirectional relationship between the Pet object and the new PetOwner and VetVisit objects, you do not need to register the new PetOwner or VetVisit objects. EclipseLink can reach these new objects through the registered Pet object and automatically detect that they are new objects.


Associating Without Reference to the Cache Object

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);

PetOwner petOwner = new PetOwner();
petOwner.setId(400);
petOwner.setName("Donald Smith");
petOwner.setPhoneNumber("555-1212");

VetVisit vetVisit = new VetVisit();
vetVisit.setId(500);
vetVisit.setNotes("Pet was shedding a lot.");
vetVisit.setSymptoms("Pet in good health.");
vetVisit.setPet(petClone);

petClone.setPetOwner(petOwner);
petClone.getVetVisits().add(vetVisit); 
uow.commit();

This executes the following proper SQL:

INSERT INTO PETOWNER (ID, NAME, PHN_NBR) VALUES (400, 'Donald Smith', '555-1212')
UPDATE PET SET PET_OWN_ID = 400 WHERE (ID = 100)
INSERT INTO VETVISIT (ID, NOTES, SYMPTOMS, PET_ID) VALUES (500, 'Pet was shedding a lot.', 'Pet in good health.', 100)

When associating new objects to existing objects, the unit of work treats the new object as if it were a clone. That is, after the commit transaction:

petOwner != session.readObject(petOwner)

Therefore, after the unit of work commit transaction, the variables vetVisit and petOwner no longer point to their respective cache objects; they point at working copy clones.

If you need the cache object after the unit of work commit transaction, you must query for it or create the association with a reference to the cache object (as described in How to Associate a New Target to an Existing Source Object in a Unidirectional Relationship: Reference to the New Cache Object After Commit Required).

If there was a bidirectional relationship between the source and target objects, you must take more care when registering them (see How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Before Commit not Required).

For more information, see Registering and Unregistering Objects).


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.

How to Associate a New Target to an Existing Source Object in a Unidirectional Relationship: Reference to the New Cache Object After Commit Required

Associating With Reference to the Cache Object example shows how to associate a new target with an existing source in a unidirectional relationship and retain a reference to the cache object.

When the Pet object is read using the unit of work, EclipseLink automatically registers it. Because there is a unidirectional relationship between the Pet object and the new PetOwner and VetVisit objects, you do not need to register the new PetOwner or VetVisit objects. EclipseLink can reach these new objects through the registered Pet object and automatically detect that they are new objects.

However, by using UnitOfWork method registerObject, you can retain a handle to the post-commit cache objects in case your code needs to continue using them after commit: for example, to display their new contents in a GUI.

If there was a bidirectional relationship between the source and target objects, you must take more care when registering them (see How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Object Before Commit Required).


Associating With Reference to the Cache Object

UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);

PetOwner petOwner = new PetOwner();
PetOwner petOwnerClone = (PetOwner)uow.registerObject(petOwner);
petOwnerClone.setId(400);
petOwnerClone.setName("Donald Smith");
petOwnerClone.setPhoneNumber("555-1212");

VetVisit vetVisit = new VetVisit();
VetVisit vetVisitClone = (VetVisit)uow.registerObject(vetVisit);
vetVisitClone.setId(500);
vetVisitClone.setNotes("Pet was shedding a lot.");
vetVisitClone.setSymptoms("Pet in good health.");
vetVisitClone.setPet(petClone);

petClone.setPetOwner(petOwnerClone);
petClone.getVetVisits().addElement(vetVisitClone); 
uow.commit();

Now, after the unit of work commit transaction:

petOwner == session.readObject(petOwner)

This means that we have a handle to the cache copy after the commit transaction, rather than a clone.

For more information, see Registering and Unregistering Objects).


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.


How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Before Commit not Required

Consider an Employee class implemented, as Employee Class example shows. Note that the setManager method modifies the Employee instance you pass into it.


Employee Class

public class Employee {

    private Collection managedEmployees = new ArrayList();
    private Emplyoee myManager;

    ...

    public setManager(Employee manager) {
        myManager = manager;
        manager.addManagedEmployee(this);
    }

    public addManagedEmployee(Employee employee) {
        managedEmployees.add(employee);
    }

    ...

}

Resolving Issues When Adding New Objects example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.

Because Employee method setManager modifies the Employee you pass in (as Employee Class example shows), you must pass in managerClone that registerObject returns.

After you call setManager, you establish the bidirectional relationship between newEmployee and managerClone. Because newEmployee is reachable from the manager object already registered with the unit of work, EclipseLink can automatically detect that it is a new object. Consequently, you do not need to register newEmployee at all and it is, in fact, an error to call registerObject on newEmployee in this case.

If your code must be able to query for the new child object prior to commit, see How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Object Before Commit Required.

If you need the cache object after the unit of work commit transaction, in this case, you must query for it.


Resolving Issues When Adding New Objects

// Get an employee read from the parent session of the unit of work
Employee manager = (Employee)session.readObject(Employee.class);

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();

// Register the manager to get its clone

Employee managerClone = (Employee)uow.registerObject(manager);

// Create a new employee
Employee newEmployee = new Employee();
newEmployee.setFirstName("Spike");
newEmployee.setLastName("Robertson");

// INCORRECT: Do not associate the new employee with the original manager. 
// This will cause a QueryException when EclipseLink detects this error during commit
//newEmployee.setManager(manager);

// CORRECT: Associate the new object with the clone. Note that in this example,
// the setManager method is maintaining the bidirectional managedEmployees 
// relationship and adding the new employee to its managedEmployees. 
// At commit time, the unit of work will detect that this is a new object 
// and will take the appropriate action
newEmployee.setManager(managerClone);

// INCORRECT: Do not register the newEmployee: this will create 
// two copies and cause a QueryException when EclipseLink detects 
// this error during commit
//uow.registerObject(newEmployee);

// Commit the unit of work
uow.commit();

For more information, see Registering and Unregistering Objects).


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.


How to Associate a New Target to an Existing Source Object in a Bidirectional Relationship: Query for Target Object Before Commit Required

Consider an Employee class implemented, as Employee Class example shows. Note that the setManager method modifies the Employee instance you pass into it.


Employee Class

public class Employee
{
    private Collection managedEmployees = new ArrayList();
    private Emplyoee myManager;

    ...

    public setManager(Employee manager)
    {
        myManager = manager;
        manager.addManagedEmployee(this);
    }

    public addManagedEmployee(Employee employee)
    {
        managedEmployees.add(employee);
    }

    ...

}

Resolving Issues When Adding New Objects example shows how to register a new object when a bidirectional relationship exists such as that between manager and employee.

Resolving Issues When Adding New Objects

// Get an employee read from the parent session of the unit of work

Employee manager = (Employee)session.readObject(Employee.class);

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();

// Register the manager to get its clone
Employee managerClone = (Employee)uow.registerObject(manager);

// Create a new employee
Employee newEmployee = new Employee();
newEmployee.setFirstName("Spike");
newEmployee.setLastName("Robertson");

// INCORRECT: Do not associate the new employee with the original manager. 
// This will cause a QueryException when EclipseLink detects this error during commit 
//newEmployee.setManager(manager);

// CORRECT: Associate the new object with the clone. Note that in this example,
// the setManager method is maintaining the bidirectional managedEmployees 
// relationship and adding the new employee to its managedEmployees. 
// At commit time, the unit of work will detect that this is a new object 
// and will take the appropriate action
newEmployee.setManager(managerClone);

// INCORRECT: Do not register the newEmployee: this will create two copies and
// cause a QueryException when EclipseLink detects this error during commit
//uow.registerObject(newEmployee);

// CORRECT: In the above setManager call, if the managerClone's
// managedEmployees was not maintained by the setManager method, then you 
// should call registerObject before the new employee is related to the manager. 
// If in doubt, you could use the  registerNewObject method to ensure that 
// the newEmployee is registered in the unit of work. The registerNewObject 
// method registers the object, but does not make a clone
uow.registerNewObject(newEmployee);

// Commit the unit of work
uow.commit();

Because Employee method setManager modifies the Employee you pass in (as Resolving Issues When Adding New Objects example), you must pass in managerClone that registerObject returns.

After you call setManager, you establish the bidirectional relationship between newEmployee and managerClone. Because newEmployee is reachable from the manager object already registered with the unit of work, EclipseLink can automatically detect that it is a new object. Consequently, you do not need to register newEmployee at all and it is, in fact, an error to call registerObject on newEmployee in this case.

If your code must be able to query for the new child object prior to commit, register the new object using UnitOfWork method registerNewObject. Unlike registerObject, this method does not create a clone.

Another difference between registerNewObject and registerObject is that registerNewObject does not cascade registration to child objects. If you call registerNewObject on a parent object, you must also call registerNewObject on new child instances if your code must be able to query for the new child object prior to commit and you prefer not to use conforming queries.

If you need the cache object after the unit of work commit transaction, you must query for it.

For more information, see Registering and Unregistering Objects).


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.


Associating a New Source to an Existing Target Object

This section describes how to associate a new source object with an existing target object with one-to-many and one-to-one relationships.

EclipseLink follows all relationships of all registered objects (deeply) in a unit of work to calculate what is new and what has changed. This is known as persistence by reachablity. In Associating a New Target to an Existing Source Object example, you saw that when you associate a new target with an existing source, you can choose to register the object or not. If you do not register the new object, it is still reachable from the source object (which is a clone, hence it is registered). However, when you need to associate a new source object with an existing target, you must register the new object. If you do not register the new object, then it is not reachable in the unit of work, and EclipseLink will not write it to the database.

Associating a New Source to an Existing Target Object example shows how to create a new Pet and associate it with an existing PetOwner.


Associating a New Source to an Existing Target Object

UnitOfWork uow = session.acquireUnitOfWork();
PetOwner existingPetOwnerClone =
        (PetOwner)uow.readObject(PetOwner.class);

Pet newPet = new Pet();
Pet newPetClone = (Pet)uow.registerObject(newPet);
newPetClone.setId(900);
newPetClone.setType("Lizzard");
newPetClone.setName("Larry");
newPetClone.setPetOwner(existingPetOwnerClone);
uow.commit();

This generates the following proper SQL:

INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (900, 'Larry', 'Lizzard', 400)

In this situation, you should register the new object and work with the working copy of the new object. If you associate the new object with the PetOwner clone without registering, it will not be written to the database.


Note: You cannot use UnitOfWork methods registerObject, registerNewObject, or registerExistingObject with an aggregate object (see Creating Relational Aggregate Descriptors). Doing so will raise a ValidationException or other errors at commit time. For more information, see How to Work with Aggregates.


If you fail to register the clone and accidentally associate the cache version of the existing object with the new object, then EclipseLink will generate an error which states that you have associated the cache version of an object ("from a parent session") with a clone from this unit of work. You must work with working copies in units of work.

For more information, see the following:


Associating an Existing Source to an Existing Target Object

This section explains how to associate an existing source object with an existing target object with one-to-many and one-to-one relationships.

As shown in Associating an Existing Source to Existing Target Object example, associating existing objects with each other in a unit of work is as simple as associating objects in Java. Just remember to only work with working copies of the objects.


Associating an Existing Source to Existing Target Object

// Associate all VetVisits in the database to a Pet from the database
UnitOfWork uow = session.acquireUnitOfWork();
Pet existingPetClone = (Pet)uow.readObject(Pet.class);
List allVetVisitClones;
allVetVisitClones = uow.readAllObjects(VetVisit.class);
Iterator iter = allVetVisitClones.elements();
while(iter.hasNext()) {
    VetVisit vetVisitClone =(VetVisit)iter.next();
    existingPetClone.getVetVisits().add(vetVisitClone);
    vetVisitClone.setPet(existingPetClone);
};
uow.commit();

The most common error when associating existing objects is failing to work with the working copies. If you accidentally associate a cache version of an object with a working copy you will get an error at commit time indicating that you associated an object from a parent session (the cache version) with a clone from this unit of work.

Associating Existing Objects example shows another example of associating an existing source to an existing target object.


Associating Existing Objects

// Get an employee read from the parent session of the unit of work

Employee employee = (Employee)session.readObject(Employee.class)

// Acquire a unit of work
UnitOfWork uow = session.acquireUnitOfWork();
Project project = (Project) uow.readObject(Project.class);

// When associating an existing object (read from the session) with a clone, 
// make sure you register the existing object and assign its clone into a unit of work

// INCORRECT: Cannot associate an existing object with a unit of work clone.
// A QueryException will be thrown
//project.setTeamLeader(employee);

// CORRECT: Instead register the existing object then associate the clone
Employee employeeClone = (Employee)uow.registerObject(employee);
project.setTeamLeader(employeeClone);
uow.commit();

For more information, see the following:

Deleting Objects

To delete objects in a unit of work, use the deleteObject or deleteAllObjects method. When you delete an object that is not already registered in the unit of work, the unit of work registers the object automatically.

When you delete an object, EclipseLink deletes the object's privately owned child parts, because those parts cannot exist without the owning (parent) object. At commit time, the unit of work generates SQL to delete the objects, taking database constraints into account.

When you delete an object, you must take your object model into account. You may need to set references to the deleted object to null (for an example, see How to Use the privateOwnedRelationship Attribute).

This section explains how to delete objects within a unit of work, including the following:


How to Use the privateOwnedRelationship Attribute

Relational databases do not have garbage collection like a Java Virtual Machine (JVM) does. To delete an object in Java you just remove the reference to the object. To delete a row in a relational database, you must explicitly delete it. Rather than tediously manage when to delete data in the relational database, use the mapping attribute privateOwnedRelationship to have EclipseLink manage the garbage collection in the relational database for you.

As shown in the Specifying a Mapping as Privately Owned example, when you create a mapping using Java, use its privateOwnedRelationship method to tell EclipseLink that the referenced object is privately owned: that is, the referenced child object cannot exist without the parent object.


Specifying a Mapping as Privately Owned

OneToOneMapping petOwnerMapping = new OneToOneMapping();
petOwnerMapping.setAttributeName("petOwner");
petOwnerMapping.setReferenceClass(com.top.uowprimer.model.PetOwner.class);
petOwnerMapping.privateOwnedRelationship();
petOwnerMapping.addForeignKeyFieldName("PET.PET_OWN_ID", "PETOWNER.ID");
descriptor.addMapping(petOwnerMapping);

When you create a mapping using Workbench, you can select the Private Owned check box under the General tab.

When you tell EclipseLink that a relationship is privately owned, you are specifying the following:

  • If the source of a privately owned relationship is deleted, then delete the target.
  • If you remove the reference to a target from a source, then delete the target.

Do not configure privately owned relationships to objects that might be shared. An object should not be the target in more than one relationship if it is the target in a privately owned relationship.

The exception to this rule is the case when you have a many-to-many relationship in which a relation object is mapped to a relation table and is referenced through a one-to-many relationship by both the source and the target. In this case, if the one-to-many mapping is configured as privately owned, then when you delete the source, all the association objects will be deleted.

Consider the Privately Owned Relationships example.

Privately Owned Relationships

// If the Pet-PetOwner relationship is privateOwned
// then the PetOwner will be deleted at uow.commit()
// otherwise, just the foreign key from PET to PETOWNER will
// be set to null.  The same is true for VetVisit
UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet)uow.readObject(Pet.class);
petClone.setPetOwner(null);
VetVisit vvClone = (VetVisit)petClone.getVetVisits().get(0);
vvClone.setPet(null);
petClone.getVetVisits().remove(vvClone);
uow.commit();

If the relationships from Pet to PetOwner and from Pet to VetVisit are not privately owned, this code produces the following SQL:

UPDATE PET SET PET_OWN_ID = NULL WHERE (ID = 150)
UPDATE VETVISIT SET PET_ID = NULL WHERE (ID = 350)

If the relationships are privately owned, this code produces the following SQL:

UPDATE PET SET PET_OWN_ID = NULL WHERE (ID = 150)
UPDATE VETVISIT SET PET_ID = NULL WHERE (ID = 350)
DELETE FROM VETVISIT WHERE (ID = 350)
DELETE FROM PETOWNER WHERE (ID = 250)

How to Explicitly Delete from the Database

If there are cases where you have objects that will not be garbage collected through privately owned relationships (especially root objects in your object model), then you can explicitly tell EclipseLink to delete the row representing the object using the deleteObject API, as shown in the Explicitly Deleting example.


Explicitly Deleting

UnitOfWork uow = session.acquireUnitOfWork();
pet petClone = (Pet)uow.readObject(Pet.class);
uow.deleteObject(petClone);
uow.commit();

The preceding code generates the following SQL:

DELETE FROM PET WHERE (ID = 100)


What You May Need to Know About the Order in which Objects Are Deleted

The unit of work does not track changes or the order of operations. It is intended to insulate you from having to modify your objects in the order the database requires.

By default, at commit time, the unit of work correctly puts in order all insert and update operations using the constraints defined by your schema. After all insert and update operations are done, the unit of work will issue the necessary delete operations.

Constraints are inferred from one-to-one and one-to-many mappings. If you have no such mappings, you can add additional constraint knowledge to EclipseLink as described in Controlling the Order of Delete Operations.



Copyright Statement