Guide  Reference  Contents  Previous  Next  Index
A persistence-capable class is one whose instances can be made persistent and saved in a database. When you define a persistence-capable class, you must consider its position in the inheritance hierarchies of the application, the range of persistent behavior that the class should support publicly and privately, and which of its fields contain persistent data. This chapter discusses the decisions you must make when defining a persistence-capable class and describes how to implement those decisions.
A persistence-capable class supports persistent operations, allowing instances of the class to act both as normal Java runtime objects and as objects stored persistently in a federated database. An application that needs to save objects in a database must define a persistence-capable class for each kind of object to be saved.
Applications may also work with Objectivity for Java persistence-capable classes, namely classes for containers and for collections of persistent objects. An application that needs to associate persistent data with a container can define its own application-defined container classes.
Descriptions of all persistence-capable classes are stored in the schema of a federated database. Several chapters in this guide discuss various aspects of working with a schema:
Every persistent object has an associated internal object called a persistor, an instance of a class that implements the PooObj interface. An object's persistor contains all the internal database states for the object and implements persistent behavior for the object.
Persistence-capable classes can support three general kinds of persistence behavior:
A persistence-capable class can implement corresponding methods to provide some or all of this persistence behavior explicitly.
A persistent event is a pre- or post-processing event; when a persistent object is involved in certain persistent operations, the object receives a persistent-event notification immediately before or after the persistent operation occurs. In response to the notification, the object can perform whatever application-specific processing is required.
Persistent events occur entirely within the Objectivity for Java process space (they are not generated asynchronously by other processes and dispatched to Objectivity for Java). These events are also session specific; that is, a persistent operation in one session affects only the persistent objects that belong to that session.
Objectivity for Java supports three kinds of persistent events:
You can make an application-defined class persistence-capable in any of four ways:
The class inherits default implementations for public methods that get and set an object's persistor, that perform persistent operations explicitly, and that handle persistent events. You do not need to implement any persistent behavior unless you want to modify the default implementation.
This interface provides public methods to get and set an object's persistor, to perform persistent operations explicitly, and to handle persistent events; you must implement all these methods.
The IooObj interface defines the persistent operations that are available to persistence-capable classes. As such, it may change from release to release. If you define classes that implement IooObj, future releases of Objectivity for Java might require you to make code changes. For example, if a new method is added to the interface, you would need to implement that method for your classes.
This interface has public methods to get and set the persistor and to handle persistent events; you need to implement those methods. If you desire, you can also implement public or private methods to perform persistent operations explicitly.
This interface has public methods to get and set the persistor; you need to implement those methods. If you desire, you can also implement public or private methods to perform persistent operations explicitly.
Once you have defined a persistence-capable class, any subclass you define from it inherits its persistence behavior.
You can also modify third-party classes to make them persistence-capable, but doing so requires care. See "Adding Persistence Capability to Third-Party Classes".
The simplest way to provide the capability for persistence is to define a class that inherits from ooObj. One drawback is that all persistence-capable classes implemented this way form a single inheritance hierarchy with ooObj at the root. If your application already contains disjoint inheritance hierarchies for the classes that you want to make persistence-capable, you can preserve the hierarchies and define the classes to implement one of the persistence-capable interfaces. The choice of which interface determines whether the class can handle persistent events and whether all persistent operations are publicly accessible.
The following table lists the four ways to make a class persistence capable and shows which capabilities are available with each.
You can make a class persistence-capable by subclassing ooObj directly, or subclassing some other application-defined class that is derived from ooObj. If you want to associate application-specific data with a container, you can subclass either ooContObj or ooGCContObj.
ExampleIn this example, Vehicle is a persistence-capable class whose superclass is ooObj; Truck is a persistence-capable class whose superclass is Vehicle.
import com.objy.db.app.ooObj; // Make class persistence-capable by inheritance public class Vehicle extends ooObj { ...
// Make class persistence-capable by inheritance public class Truck extends Vehicle { ... }
The ooObj class provides the following default handling for persistent events.
If you want your class to respond differently to any of these persistent events, you must implement the appropriate behavior as described in "Handling Persistent Events".
Unless your persistence-capable class is a descendant of ooObj, you must implement methods to get and set an object's persistor.
First, decide where to cache each object's persistor. The simplest and most efficient approach is to store the persistor in a field of type PooObj. (The ooObj class uses this approach.) The field holding the persistor must be transient so that it is not stored as part of the data of a persistent object; see "Transient Fields". If the persistor field is not transient, a schema exception will be thrown the first time the class is registered, an object of the class is stored, or an index is defined for objects of the class.
Alternatively, you could cache the persistor in some global (non-persistent) object, such as an array, vector, or hash table.
When a persistence-capable class is instantiated, its persistor must be initialized to null. A null persistor indicates that the newly created object is transient. When the object becomes persistent, Objectivity for Java gives it a newly created persistor. If the object becomes dead, Objectivity for Java sets its persistor to a dead persistor; see "Dead Persistent Objects".
You need to define the two methods declared in the Persistent interface:
The implementations of both methods must be synchronized for thread safety. You can refer to the Objectivity for Java class ooObj for an example implementation of these methods.
ExampleThis Vehicle class is made persistent by implementing Persistent; its methods to get and set an object's persistor follow the model set forth by ooObj.
import com.objy.db.iapp.PooObj; import com.objy.db.iapp.Persistent; public class Vehicle implements Persistent { // Get and set the persistor private transient PooObj persistor; public synchronized PooObj getPersistor() { return persistor; } public synchronized void setPersistor(PooObj persistor) { this.persistor = persistor; } ... }
You need to implement handling for persistent events in any of the following circumstances:
A persistence-capable class that can respond to persistent events has a handler method corresponding to each kind of persistent event. A persistent object is notified that a persistent event has occurred by a call to the appropriate handler method. The handler method performs whatever application-specific processing is required to respond to the event.
A persistent object's activate method handles activate events. This method is called after the object is fetched. An activate event is triggered after execution of the fetch or markModified method of the object's persistor if the object's data had not already been fetched.
You might use the activate method to set appropriate values for transient fields or to handle deleted references intelligently.
A persistent object's deactivate method handles deactivate events. This method is called for all objects belonging to a session after the session's current transaction is successfully committed or aborted. If an object is made persistent during a transaction that is subsequently aborted, it is still sent a deactivate event.
You might use the deactivate method to allow the application to take different actions depending on whether a transaction is committed or aborted. Doing so can be useful for a user interface or in a work-flow application where an aborted transaction affects the actions of the application.
A persistent object's preWrite method handles pre-write events. This method is called before the object is written to the database. A pre-write event can be triggered by any method that causes the object to be written, namely:
You might use the preWrite method to transform or encrypt the values of some persistent fields. Alternatively, your method could check that values in the various persistent fields are mutually consistent; if it finds a problem, it could throw a runtime exception to prevent Objectivity for Java from writing out this particular object. In the latter case, the exception would abruptly terminate the encompassing operation.
Your handler methods may perform whatever application-specific processing is required in response to a persistent event.
The parameter to a handler method is a read-only information object of a class that implements the PersistentEventInfo interface. The information object contains information specific to the persistent event that occurred, for example, why the event was triggered.
You should follow these guidelines when you implement your handler methods:
A number of rules govern how Objectivity for Java handles uncaught exceptions thrown by persistent-event handler methods. When exceptions are thrown, it is important to define the state of the objects, whether they are marked as modified or as requiring to have their data fetched.
Any exception thrown by an object's activate method can affect the state of that object. The following table lists methods that can trigger an activate event and shows the resulting object states and the exception propagation when the activate method throws an exception.
Since more than one object may be notified of this event at any one time, it is not reasonable for Objectivity for Java to stop whenever some object's deactivate method throws an exception. Instead, Objectivity for Java silently consumes such exceptions and continues to notify the remaining objects.
A deactivate event is a courtesy notification in that any exceptions thrown by notified objects' deactivate methods do not affect the commit or abort operation that triggered the event. The database operation completes and the objects are updated according to their fetched and modified state.
If an aborted transaction triggered the event:
An object deleted during a transaction is removed from the session, so its deactivate method is not called when the transaction is committed or aborted. If the transaction is aborted, the deleted objects still exist in the database. However, its corresponding Java object has been marked dead; the object needs to be retrieved again before the application can access it.
The preWrite method is called for each object that is written by the persistent operation that triggered the pre-write event. If some object's preWrite method throws an exception, the operation terminates abruptly; the state of the object throwing the exception is not changed (that is, it is still marked as modified) and the exception is propagated. If the operation involves other objects, the state of each object depends on whether it is written to the cache prior to the exception or after the exception.
The following table illustrates the object states if an exception is thrown.
The methods in the IooObj interface explicitly provide the full range of persistence behavior that is available implicitly through an object's persistor. If your class implements IooObj, you must implement all these methods. If your class instead implements Persistent or PersistentEvents, you may implement any of these methods that you choose.
Your implementation of an explicit persistence method should take the appropriate action if the object is transient or if it is a dead object.
The Vehicle class implements the IooObj interface. Its markModified and fetch methods forward the call, when appropriate, to the object's persistor.
import com.objy.db.iapp.IooObj; import com.objy.db.iapp.PooObj; public class Vehicle implements IooObj { private transient PooObj persistor = null; ... // Explicit persistence behavior void fetch() { if (persistor.isDead()) throw new ObjectIsDeadException( "Attempted persistent operation on dead object"); // Do nothing if object is transient if (persistor != null) persistor().fetch(); } void markModified() { if (persistor.isDead()) throw new ObjectIsDeadException( "Attempted persistent operation on dead object"); // Do nothing if object is transient if (persistor != null) persistor().markModified(); } ... }
To ensure that your class accesses the persistor appropriately, you can copy the implementation of the various persistence methods from ooObj to your class. Note, however, that ooObj uses internal methods that throw exceptions if the object is transient or dead. If you copy implementations from ooObj, be sure to copy definitions of these internal methods as well.
ExampleThis Vehicle class implements the IooObj interface. Its persistence methods, copied from ooObj, use the internal methods persistor and notDeadPersistor.
import com.objy.db.iapp.IooObj; import com.objy.db.iapp.PooObj; public class Vehicle implements IooObj { // Get and set the persistor ... // Internal methods private synchronized PooObj persistor() { if (persistor == null) throw new ObjectNotPersistentException( "Attempted persistent operation on transient object"); if (persistor.isDead()) throw new ObjectIsDeadException( "Attempted persistent operation on dead object"); return persistor; } private synchronized PooObj notDeadPersistor() { if (persistor.isDead()) throw new ObjectIsDeadException( "Attempted persistent operation on dead object"); return persistor; } // Explicit persistence behavior void fetch() { if (persistor != null) notDeadPersistor().fetch(); } public void write() { persistor().write(); } ... }
Classes that implement either Persistent or PersistentEvents need not define persistence methods. However, each time an object of such a class needs to perform a persistent operation, it must test that its persistor is valid for the desired operation. You avoid repeating the necessary tests with each call and avoid implementing persistent methods for several classes by defining a "delegator" class whose sole purpose is to provide persistence behavior.
This example shows a few methods of a class Delegator whose role is to implement persistence behavior. Any number of persistence-capable classes that implement Persistent or PersistentEvents could use the Delegator class. The complete class definition appears in the Delegator programming example.
public class Delegator { // Internal methods private static synchronized PooObj notDeadPersistor( PooObj persistor) { if (persistor.isDead()) throw new ObjectIsDeadException( "Attempted persistent operation on dead object"); return persistor; } ... // Explicit persistence behavior public static void markModified(PooObj persistor) { if (persistor != null) notDeadPersistor(persistor).markModified(); } ... }
It is possible to add persistence capability to any class by implementing one of the persistence-capable interfaces. Before doing so however, you should keep in mind the following caveats:
Regardless of how you make your class persistence-capable, you will follow the same approach when defining fields of the class. Fields can serve two roles for an object. They can capture the state associated with an object or they can link an object to other objects. Persistent objects can have persistent fields, whose values are saved in the database, and transient fields, whose values are not saved.
All non-static and non-final fields you define for a persistence-capable class are persistent by default. The values in the persistent fields of a persistent object constitute that object's persistent data. When the object is written to the federated database, the values in those fields are saved persistently.
Every persistent field must be of one of the following data types:
Category | Types |
---|---|
Java primitive type | char byte short int long float double boolean |
Java string class | String StringBufferNote: If you set the value of a String persistent field to be an empty string (""), the field will be stored in the database as null. When the object containing the String field is later read back, the field in the Java object will likewise be set to null. |
Java date or time class | java.util.Date java.sql.Date java.sql.Time java.sql.TimestampNote: An object of a date/time class in a persistent field is stored in the federated database as an internal persistent object. |
Persistence-capable class | ooObj An application-defined persistence-capable class A persistent-collection class A container class Any interfaceNote: Although the declared type of a persistent field may be any interface, the actual object referenced by the field must be a persistent object. |
Java array of any of the preceding types | For example, long[] or String[]Note: An array in a persistent field is stored in the federated database as an internal persistent object. Each element of a String array is also stored as an internal persistent object. |
An Objectivity for Java relationship | See Chapter 7, "Relationships" for information on relationships. |
This example illustrates the persistent fields of a class called Vehicle.
public class Vehicle extends ooObj { // Persistent fields protected String license; protected String type; protected int doors; protected int transmission; protected boolean available; ... }
As the preceding table indicates, you cannot create persistent fields that reference the database and federated database where an object is stored, the session to which the object belongs, or the object's identifier. You can, however, obtain this information through methods defined on ooObj (or on the object's persistor).
ExampleThis code fragment illustrates the methods for retrieving the Objectivity for Java properties of a persistent object that implements explicit persistence behavior. The complete method definition appears in the RentalFields.Vehicle programming example .
public static void printInfo(Vehicle vehicle) { // This method must be called during a transaction // Get vehicle's container ooContObj cont = vehicle.getContainer(); // Get vehicle's database ooDBObj db = cont.getDB(); // Get vehicle's federated database ooFDObj fd = db.getFD(); // Get vehicle's session Session session = vehicle.getSession(); // Get vehicle's object identifier ooId oid = vehicle.getOid(); ... }
If your Java application will interoperate with applications written in C++ and/or Smalltalk, you must select field types that will map to Objectivity/DB data types that are supported by the other languages. For more information on this topic, see Chapter 19, "Schema Matching for Interoperability".
Your class can also have transient fields, whose values are not saved when an object is written to the database. To specify that a field is transient, simply give it the transient modifier when you define your class. Transient fields are not modified when the object is read from the database or copied when a persistent object is copied. You can, however, set the value of a transient field after the object is read from the database.
You must define a field as transient in either of the following circumstances:
This example illustrates a transient field dailyRate of the class Vehicle.
public class Vehicle extends ooObj { ... // Transient field protected transient int dailyRate; ... }
Many applications work with object graphs, directed graph data structures that consist of objects linked to other objects. Objectivity/DB provides three mechanisms for linking objects together:
As in any Java application, you can use fields containing object references to link objects together. A field whose type is a persistence-capable class can represent a link to the object referenced by that field. A field whose type is a Java array of objects of a persistence-capable class can represent links to the objects in the array.
ExampleIn this example, the Vehicle class has a persistent field fleet to link a vehicle to its rental fleet. The Fleet class has a persistent field vehicles containing a fixed-sized array of one thousand vehicles; this field serves to link a rental fleet to all the vehicles in the fleet.
public class Vehicle extends ooObj { // Persistent fields ... protected Fleet fleet; ...
public class Fleet extends ooObj { static final int FLEET_SIZE = 1000; // Persistent fields protected Vehicle[] vehicles = new Vehicle[FLEET_SIZE]; ... }
Objects can also be linked together by their membership in persistent collections. A persistent collection can be saved directly (for example, as a named root), or it can be referenced in a persistent field of a persistent object.
You can use a persistent collection to link one object to a group of objects. Instead of defining a field containing a Java array of persistent objects, you can define a field containing a persistent collection. One advantage of collections is that they are of variable size, whereas a Java array's size is fixed. A collection can grow or shrink as needed.
ExampleIn this example, the vehicles field of the Fleet class has been replaced by a field of type ooMap. Instead of an array of one thousand elements, the rental fleet's vehicles field now contains a map that associates each vehicle in the fleet with an identifying string, such as its license ID. At any time, the map contains only as many vehicles as are in the fleet, which may be more or less than one thousand.
public class Fleet extends ooObj { // Persistent fields protected ooMap vehicles = new ooMap(); ... }
See Chapter 9, "Persistent Collections" for additional details about persistent collections.
Objectivity/DB provides a mechanism called relationships as an alternative way to link objects together. Objectivity/DB relationships provide a higher level of functionality than referencing objects directly from persistent fields. You can specify the directionality and cardinality of relationships, whether operations on objects propagate along relationships, and how relationships are handled when you create a new copy or a new version of an object. See Chapter 7, "Relationships," for a complete discussion of Objectivity/DB relationships and how to define and use relationships in Objectivity for Java.
ExampleThis example substitutes bidirectional relationships for the fleet and vehicles fields in the preceding example. The Vehicle class has a one-to-one relationship fleet that relates a vehicle to its fleet. The Fleet class has a one-to-many relationship vehicles that relates a rental fleet to the vehicles it contains. The two relationships are inverses; that is, if a given vehicle's fleet relationship links it to a given fleet, that fleet's vehicles relationship links the fleet to the vehicle.
package RentalRelations; import com.objy.db.app.*; ... public class Vehicle extends ooObj { ... // Relationships private ToOneRelationship fleet; protected static ManyToOne fleet_Relationship() { return new ManyToOne( "fleet", // This relationship "RentalRelations.Fleet", // Related class "vehicles", // Inverse Relationship.INLINE_NONE); // Store non-inline }
package RentalRelations; import com.objy.db.app.*; public class Fleet extends ooObj { // Relationships private ToManyRelationship vehicles; protected static OneToMany vehicle_Relationship() { return new OneToMany( "vehicles", // This relationship "RentalRelations.Vehicle", // Related class "fleet", // Inverse Relationship.INLINE_NONE); // Store non-inline } }
An array field containing persistent objects has performance overhead relative to a persistent collection field or a relationship. Because the array is part of the persistent data of the containing object, the entire array and all its elements are read from the federated database when you fetch the containing object's data. Similarly, the entire array and all its elements are written to the federated database when you write the containing object's data--even if you did not modify the array or any of the persistent objects it contains.
In contrast, if you use a persistent collection or a relationship to link objects together, the destination objects are read only if they are accessed and they are written only if they are modified.
When you retrieve a persistent object, you obtain an empty, unlocked object. You need to fetch the object's data before you can safely access the object's persistent fields or relationships; the methods that fetch data also lock the object. You can ensure that objects of your class are used safely by accessing fields and relationships only through access methods that fetch data and obtain locks as necessary.
An additional advantage of using access methods is that they hide the implementation you have chosen, which simplifies the update process if you change your implementation during the prototyping or development phases of your project. For example, suppose you decide to replace a field with a relationship or vice versa. You would need to reimplement only your access methods; the code that calls the access methods would remain unchanged. This kind of implementation change modifies the class description in the schema. As a consequence, if you change the implementation of your class after you have deployed your application, you will need to provide a conversion application to convert objects in the federated database from the old implementation to the new implementation. See Chapter 15, "Schema Evolution and Object Conversion".
If your persistence-capable class is derived from ooObj or implements IooObj, your access methods can call the fetch and markModified methods of the persistent object. If your persistence-capable class instead implements the Persistent or PersistentEvents interface, your access methods must call the fetch and markModified methods of the object's persistor. The easiest approach is to implement fetch and markModified methods for your class as described in "Providing Explicit Persistence Behavior". The following descriptions assume that all your persistence-capable classes have fetch and markModified methods.
You should define field access methods for every persistent field of a class and call those methods whenever you get or set the value of a persistent field.
If a persistent field has a scalar type, you can define one access method to get the scalar value in the field. If the value in the field can be changed, you can define another access method to set the value in the field.
ExampleThis example illustrates some field access methods for the Vehicle class. Each persistent field has an access method that gets the value of that field; the getLicense method illustrates the form of these methods. The setFleet access method sets the fleet field to the specified fleet. All other persistent fields are initialized when a vehicle object is created; only the available field can be modified after initialization. The field access methods rentVehicle and returnVehicle set the Boolean field available to false and true, respectively. The complete class definition appears in the RentalFields.Vehicle programming example .
// Field access methods to get persistent field values public String getLicense() { fetch(); return this.license; } ... // Field access methods to set fields public void setFleet(Fleet fleet) { // Set fleet field markModified(); this.fleet = fleet; } public void rentVehicle() { // Set available field markModified(); this.available = false; } public void returnVehicle() { // Set available field markModified(); this.available = true; }
If your class that implements the Persistent or PersistentEvents interface uses a delegator class instead of implementing explicit persistence behavior (see "Delegating Persistent Operations"), then your field access methods should call the static fetch and markModified methods of the delegator class.
ExampleThe following field access method uses a delegator. The complete class definition appears in the PersistentInterface.Vehicle programming example .
// Field access methods to get persistent field values public void getLicense() { Delegator.fetch(this.getPersistor()); return this.license; } public void setFleet(Fleet fleet) { // Set fleet field Delegator.markModified(this.getPersistor()); this.fleet = fleet; }
If a persistent field contains a Java array, you can define access methods to get and set the value at a particular array index. If necessary, you can also define an access method to return the array itself.
ExampleThis example illustrates access methods for a persistent field containing an array. The getVehicle method gets the value at a particular array index and the setVehicle method sets the value at a particular index. The complete class definition appears in the RentalFields.SimpleFleet programming example .
public Vehicle getVehicle(int n) { fetch(); return this.vehicles[n]; } public void setVehicle( int n, Vehicle newMember) { markModified(); this.vehicles[n] = newMember; }
An alternative approach to accessing array fields directly is to hide the implementation with field access methods that manage the array.
ExampleIn this example, a new persistent field, numberOfVehicles, keeps track of the number of vehicles in the fleet. The addVehicle method adds a vehicle to the fleet; the deleteVehicle method deletes a vehicle; the findVehicle method gets the vehicle with the specified license ID. The getAllVehicles method gets an enumeration that finds all vehicles in the fleet; this method uses an inner Enumeration class called VehicleItr. The complete class definition appears in the RentalFields.Fleet programming example .
public void addVehicle(Vehicle newMember) { markModified(); if (findVehicle(newMember.getLicense()) == null) { this.vehicles[this.numberOfVehicles] = newMember; this.numberOfVehicles++; } } public void deleteVehicle(Vehicle vehicle) { markModified(); int i = 0; while ((i < this.numberOfVehicles) && (this.vehicles[i] != vehicle)) { i++; } if (i != this.numberOfVehicles) { // Vehicle was found; remove it for (int j = i + 1; j < this.numberOfVehicles; j++, i++) this.vehicles[i] = this.vehicles[j]; this.numberOfVehicles--; } } public Vehicle findVehicle(String license) { fetch(); if (this.numberOfVehicles == 0) return null; for (int i = 0; i < this.numberOfVehicles; i++) { if (this.vehicles[i].getLicense().equals(license)) return this.vehicles[i]; } return null; } public Enumeration getAllVehicles() { fetch(); return new VehicleItr(this); }
If a persistent field contains a persistent collection, your field access methods can get and set the persistent collection. An alternative approach is to hide the implementation you have chosen for the field. Instead of methods that get and set the entire collection, you can define access methods that get and set elements of the collection.
ExampleThis example illustrates access methods for a persistent field containing a map. The addVehicle method adds a vehicle to the map; the deleteVehicle method deletes a vehicle; the findVehicle method gets the vehicle with the specified license ID; the getAllVehicles method returns an iterator that finds all vehicles in the map. Note that the access methods that modify the map call fetch rather than markModified. The call to fetch retrieved the fleet's persistent data, including the map; the map itself obtains the necessary locks and ensures that the map is written if it is modified. The complete class definition appears in the RentalMap.Fleet programming example.
public void addVehicle(Vehicle newMember) { fetch(); String key = newMember.getLicense(); if (this.vehicles.isMember(key)) return; this.vehicles.add(newMember, key); this.numberOfVehicles++; } public void deleteVehicle(Vehicle vehicle) { fetch(); String key = vehicle.getLicense(); if (this.vehicles.isMember(key)) { this.vehicles.remove(key); this.numberOfVehicles--; } } public Vehicle findVehicle(String license) { fetch(); if (this.vehicles.isMember(license)) // Cast retrieved object to class Vehicle return (Vehicle)this.vehicles.lookup(license); else return null; } public Iterator getAllVehicles() { fetch(); return this.vehicles.elements(); }
Chapter 7, "Relationships," describes how you specify and work with relationships. Briefly, a class has a special field for each relationship; when an object of the class is made persistent, the field is automatically initialized to contain a relationship (of one of the classes ToOneRelationship or ToManyRelationship). You call methods of that relationship to link the object to related objects and to get its related objects.
To ensure that relationships are used correctly, you should define relationship access methods for every relationship of a class and call those methods whenever you need to get related objects or establish relationships with other objects.
This example illustrates access methods for a to-one relationship. The setFleet method sets the related fleet; the getFleet method gets the related fleet. The complete class definition appears in the RentalRelations.Vehicle programming example .
// Relationship access methods public void setFleet(Fleet fleet) { fetch(); this.fleet.clear(); // Remove any existing relationship this.fleet.form(fleet); } public Fleet getFleet() { fetch(); return (Fleet)this.fleet.get(); // Cast to Fleet }
This example illustrates access methods for a to-many relationship. The addVehicle method adds a related vehicle; the deleteVehicle method removes a vehicle from the relationship; the findVehicle method gets the related vehicle with a particular license ID; the getAllVehicles method returns an iterator that finds all related vehicles. The complete class definition appears in the RentalRelations.Fleet programming example .
// Relationship access methods public void addVehicle(Vehicle newMember) { fetch(); if (this.vehicles.includes(newMember)) return; this.vehicles.add(newMember); this.numberOfVehicles++; } public void deleteVehicle(Vehicle vehicle) { fetch(); if (this.vehicles.includes(vehicle)) { this.vehicles.remove(vehicle); this.numberOfVehicles--; } } public Vehicle findVehicle(String license) { fetch(); String predicate = new String("license == \"" + license + "\""); Iterator itr = this.vehicles.scan(predicate); if (itr.hasNext()) return (Vehicle)itr.next(); // cast to Vehicle else return null; } public Iterator getAllVehicles() { fetch(); return this.vehicles.scan(); }
You can define any methods for your persistence-capable classes that your applications require. Note, however, that Objectivity/DB saves only data persistently, not methods. Thus, if more than one application needs to use persistent objects of a given class, each application must have a definition of that class that includes both declarations for its persistent fields and implementations for its application-defined methods.
Guide  Reference  Contents  Previous  Next  Index