|
How To / Managing Relationships |
The hardest thing with persistent objects is managing the relationships between them.
This covers the basic approaches for this, regardless of whether the persistence engine
natively supports them
Contents
- Native Support
- Non-Encapsulated
- Encapsulated
|
Native Support |
- The persistence engine transparently loads the related objects (as specified in the
mapping files), when an object is loaded from the database. If lazy loading is specified,
then the related objects will utilize virtual proxies, and will be loaded on demand. Additions,
updations and deletions of related objects will also be natively supported.
- This kind of capability requires a well-designed normalized database, which uses object ids
as primary keys. Refer to the Scott Ambler paper (http://www.ambysoft.com/persistenceLayer.pdf)
for more information.
- Castor is an example of such a persistence engine. But currently, it is not supported.
- Example - Assume that the Part table has an aggregate relationship with the Item table (this will
have to specified in the mapping file, in ways as yet undefined). The Part domain object will contain
a Collection of related Item domain objects. This Collection will be filled with appropriate objects
by the persistence engine, transparently, whenever a Part object is loaded. The Part object will
support the following methods, in addition to the regular get/set/update/validate methods for the
various fields.
Example Domain Object Code |
public Collection getItemCol() {
return m_itemCol;
}
public void addItem(Item item) {
m_itemCol.add(item);
}
public void deleteItem(Item item) {
m_itemCol.remove(item);
}
|
- To print out the Serial for each Item for a Part, the client class will have the following code:
Example Query |
UOW uow = new UOW();
Criteria c = new Criteria();
c.setTable(PartMeta.getName());
c.addCriteria(PartMeta.PART_NO, "P1");
Iterator parts = uow.query( c ).iterator();
if (parts.hasNext()) {
Part part = (Part) itr.next();
for (Iterator items = part.getItemCol().iterator; items.hasNext(); )
System.out.println( ((Item) items.next()).getSerial() );
}
|
Note: The current JDBC Engine does not support native relationship management, however the
original prototype persistence engine based on Castor did, as this was a feature of Castor.
|
Non-Encapsulated |
- This is the case when the persistence engine provides no support for managing relationships. The client classes will have to manually add/update/delete related objects for a domain object.
- To print out the Serial for each Item for a Part, the client class will have the following code-
Example Query |
UOW uow = new UOW();
Criteria c = new Criteria();
c.setTable(PartMeta.getName());
c.addCriteria(PartMeta.PART_NO, "P1");
Iterator parts = uow.query( c ).iterator();
if (parts.hasNext()) {
Part part = (Part) itr.next();
Criteria c1 = new Criteria();
c1.setTable(ItemMeta.getName());
c1.addCriteria(ItemMeta.PART_NO, part.getPartNo());
Iterator items = uow.query(c1).iterator();
while (items.hasNext())
System.out.println( ((Item) items.next()).getSerial() );
}
|
Note: This may be the best solution if you are 'persistence enabling' and existing legacy data
model, where the relationships are very 'ad-hoc' and the model is not well normalized.
Note: This is the strategy that we have currently implemented in the V1.0 Domain Object Pattern.
See the Domain Object How To for more details.
|
Encapsulated |
- This is the cross of the above 2 methods. The persistence engine still provides no support for managing relationships. However, the relationship can be built into the domain classes, manually.
- Continuing with the above example, the Part domain object will contain a Collection of related Item domain objects. However, this Collection will have to be filled manually with appropriate objects. The Part object will support the following methods, in addition to the regular get/set/update/validate methods for the various fields.
Example Domain Object Code |
public Collection getItemCol() {
if (m_itemCol == null) {
Criteria c = new Criteria();
c.setTable(ItemMeta.getName());
c.addCriteria(ItemMeta.PART_NO, this.getPartNo());
m_itemCol = this.getUOW().query(c);
}
return m_itemCol;
}
public void addItem(Item item) {
if (m_itemCol == null)
m_itemCol = new ArrayList();
m_itemCol.add(item);
}
public void deleteItem(Item item) {
if (m_itemCol == null || ! m_itemCol .contains(item))
throw new DeleteFailedException();
m_itemCol.remove(item);
}
|
- To print out the Serial for each Item for a Part, the client class will have the following code:
Example Query |
UOW uow = new UOW();
Criteria c = new Criteria();
c.setTable(PartMeta.getName());
c.addCriteria(PartMeta.PART_NO, "P1");
Iterator parts = uow.query( c ).iterator();
if (parts.hasNext()) {
Part part = (Part) itr.next();
for (Iterator items = part.getItemCol().iterator; items.hasNext(); )
System.out.println( ((Item) items.next()).getSerial() );
}
|
Note: If you plan to migrate your code between a non-native and native persistence engine, then it
is recommended that you use some kind of pattern and code generation engine, such that you can
initially generate all your domain objects in an encapsulated fashion for the non-native engine,
and at a later date re-generate them will a modified pattern that remove the encapsulated code so
they will run on a native engine.
|
|