To be stored in a database, an object must be persistence-capable. This section provides information about how to explicitly define persistence-capable classes in your program without using the automated class file postprocessor supplied with PSE/PSE Pro. Object Design recommends that you use the automated postprocessor. However, you might choose this manual method if you want to
Explicitly Defining Persistence-Capable Classes
Additional Information About Manual Annotation
Creating and Accessing Fields in Annotations
Every class inherits from the Object class, which defines the hashCode() method and provides a default implementation. For a persistent object, this default implementation almost always returns a different value for the same persistent object (the object on the disk) at different times. This is because PSE/PSE Pro fetches the persistent object into different Java objects at different times (in different transactions or different invocations of Java).
This is not a problem if you never put the object into a persistent hash table or other structure that uses the hashCode() method to locate objects. If you do put them in hash tables or something similar, the hash table or other structure that relies on the hashCode() method might become corrupted when you bring the objects back from the database. There are two ways to solve this problem:
public void initializeContents(GenericObject genObj)Here is an example:
public void initializeContents(GenericObject handle) {
name = handle.getStringField(1, PCI);
age = handle.getIntField(2, PCI);
children = (Person[])handle.getArrayField(3, PCI);
}
If the class you are annotating has a superclass other than COM.odi.Persistent, you must also initialize superclass fields by invoking initializeContents() on the superclass.
public void flushContents(GenericObject genObj)Here is an example:
public void flushContents(GenericObject handle) {
handle.setClassField(1, name, PCI);
handle.setIntField(2, age, PCI);
handle.setArrayField(3, children, PCI);
}
If the class you are annotating has a superclass other than COM.odi.Persistent or COM.odi.util.HashPersistent then you must also flush superclass fields by invoking flushContents() on the superclass.
public void clearContents()Here is an example:
public void clearContents() {
name = null;
age = 0;
children = null;
}
If the class you are annotating has a superclass other than COM.odi.Persistent or COM.odi.util.HashPersistent then you must also clear superclass fields by invoking clearContents() on the superclass.public int hashCode()
Modify the methods that reference nonstatic fields to call the Persistent.fetch() and Persistent.dirty() methods as needed. While this step is not mandatory, it does provide a systematic way to ensure that the application calls the fetch() or dirty() method before accessing or updating object contents.
public Persistent create() { return new Person(this); }
This should call a constructor, referred to as a hollow object constructor, that leaves fields in the default state. For an abstract class, the create() method can return null.
public Object createArray (int nDimensions,
int sizeOfOuterDimension)
throws BadArrayDimensionsException
This should be prepared to create arrays with as many dimensions as your application requires.
public Class getClassDescriptor() {
return Class.forName("COM.odi.demo.people.Person"); }
public Field[] getFields() { return fields; }
private static Field[] fields = {
Field.createString("name"),
Field.createInt("age"),
Field.createClassArray("children", "Person", 1)
};
The definition of the getFields() method can specify create methods for fields that are not in the class definition and can omit create methods for fields that are in the class definition. package COM.odi.demo.people;
import COM.odi.*;
// Define a class that inherits from Persistent:
public
class Person extends Persistent {
// Fields:
String name;
int age;
Person children[];
static ClassInfo myClassInfo;
...
// Constructor:
public Person(String name, int age, Person children[]) {
this.name = name; this.age = age; this.children = children;
}
// Hollow object constructor:
public Person(ClassInfo) { }
// Accessor methods that have been modified to call
// the fetch() and dirty() methods:
public String getName() { fetch(this); return name; }
public void setName(String name) {dirty(this); this.name = name; }
public int getAge() { fetch(this); return age; }
public void setAge(int age) { dirty(this); this.age = age; }
public Person[] getChildren() { fetch(this); return children; }
public void setChildren(Person children[]) {
dirty(this); this.children = children;
}
...
// Additions required for PSE/PSE Pro:
// Define the initializeContents() method to load real
// values into hollow persistent objects, which makes
// them active persistent objects:
public void initializeContents(GenericObject handle) {
name = handle.getStringField(1, PCI);
age = handle.getIntField(2, PCI);
children = (Person[])handle.getArrayField(3, PCI);
}
// Define the flushContents() method to copy the
// content of a persistent object to the database:
public void flushContents(GenericObject handle) {
handle.setClassField(1, name, myClassInfo);
handle.setIntField(2, age, myClassInfo);
handle.setArrayField(3, children, myClassInfo);
}
// Define the clearContents() method to reset the values
// of a persistent instance to the default values.
// This method must set all reference fields that
// referred to persistent objects to null:
public void clearContents() {
name = null;
age = 0;
children = null;
}
// Create an instance of the subclass of ClassInfo and
// register that instance:
static {myClassInfo=new PersonClassInfo();
ClassInfo.register(myClassInfo); }
}
// Define the subclass of ClassInfo. A recommended naming
// convention is to prefix the name of your persistence-capable
// class to "ClassInfo".
class PersonClassInfo extends ClassInfo {
// Define a create() method to create instances of your
// Persistent subclass with default field values. The method
// calls the hollow object constructor and passes this,
// which is an instance of the ClassInfo subclass:
public Persistent create() { return new Person(this); }
// Define a createArray() method to create an array of references to
// instances of your Persistent subclass:
public Object[] createArray(int nDimensions,
int sizeOfOuterDimension)
throws BadArrayDimensionsException {
switch (nDimensions) {
case 1:
return new Person[sizeOfOuterDimension];
case 2:
return new Person[sizeOfOuterDimension][];
default:
throw new BadArrayDimensionsException(nDimensions);
}
}
// Define these public methods to provide access to
// the name of the persistence-capable class, the name of its
// superclass, and the names of its fields.
// The array returned by getFields() must contain the
// fields in the order of their field numbers.
public Class getClassDescriptor() { return
Class.forName("COM.odi.demo.people.Person"); }
public Field[] getFields() { return fields; }
private static Field[] fields = {
Field.createString("name"),
Field.createInt("age"),
Field.createClassArray("children", "Person", 1)
};
}
You must create a subclass of the ClassInfo class for each class that you define that you want to be persistence-capable. ClassInfo is an abstract class for managing schema information for persistence-capable classes. PSE/PSE Pro requires the schema information to manage the object.After you perform the steps described in this section, you can store instances of your Persistent subclass in a database.
PSE/PSE Pro does not let you store final instance variables persistently. This is because it is not possible to write the initializeContents() and clearContents() methods to correctly handle final instance variables.
class A {
transient java.awt.Component myVisualizationComponent;
int myValue;
...
}
In this class, the myVisualizationComponent field is declared to be a transient reference to java.awt.Component. java.awt is a package containing GUI classes that do not lend themselves to being persistence-capable. package COM.odi.demo.rep;
/**
* A Rectangle has two Points, representing its upper-left
* and lower-right corners. However, its persistent representation
* is formed by storing the x and y coordinates of the two points,
* rather than the points themselves. This demonstrates the control
* that the definer of a persistent class has over the persistent
* representation. Note that Identity of the Point objects is not
* preserved, since the Point objects are not persistent objects.
*/
import COM.odi.*;
public class Rectangle extends Persistent {
Point a;
Point b;
ClassInfo myClassInfo;
...
public void initializeContents(GenericObject handle) {
a = new Point(handle.getIntField(1, myClassInfo),
handle.getIntField(2, myClassInfo));
b = new Point(handle.getIntField(3, myClassInfo),
handle.getIntField(4, myClassInfo));
}
public void flushContents(GenericObject handle) {
handle.setIntField(1, a.x, myClassInfo);
handle.setIntField(2, a.y, myClassInfo);
handle.setIntField(3, b.x, myClassInfo);
handle.setIntField(4, b.y, myClassInfo);
}
...
}
...
}
class RectangleClassInfo extends ClassInfo
{
...
public Field[] getFields() { return fields; }
private static Field[] fields =
{ Field.createInt("ax"),
Field.createInt("ay"),
Field.createInt("bx"),
Field.createInt("by"), };
}
class CloneableInfo extends ClassInfo
{
/* Cannot create instances of an interface; */
public COM.odi.Persistent create() { return null; }
public Object[] createArray(int nDimensions,
int sizeOfOuterDimension)
throws BadArrayDimensionsException {
switch(nDimensions) {
case 1:
return new Cloneable[sizeOfOuterDimension];
default:
throw new BadArrayDimensionsException(nDimensions);
}
}
public Class getClassDescriptor() throws
ClassNotFoundException {
return Class.forName("java.lang.Cloneable");
}
public Field[] getFields() { return null; }
}
For example, if you define the Boat class, the name of the associated subclass of ClassInfo must be BoatClassInfo.
Now, suppose you define the following two classes:
abstract class Y {
int yValue;
abstract void doSomething();
}
class X extends Y {
float xValue;
void doSomething() {}
}
Class Y must have an associated ClassInfo subclass and class X must have an associated ClassInfo subclass. Both subclasses extend ClassInfo. The ClassInfo subclass associated with X does not extend the ClassInfo subclass associated with Y.Similarly, in the ClassInfo subclass for X, the Field array must include only those fields defined explicitly in X; XClassInfo.getFields() must report only the immediate persistent fields in X. The ClassInfo subclass for Y defines a Field array that contains the fields explicitly defined in Y.
To execute a fetch() or dirty() call, PSE/PSE Pro first checks whether or not a fetch() or dirty() call was already invoked on the object in the current transaction. If it was, PSE/PSE Pro does nothing and the program continues. If it was not, PSE/PSE Pro executes the method.
For an object that was not already retrieved, PSE/PSE Pro copies the contents of the object from the database into the GenericObject instance. It then passes this instance to the initializeContents() method defined in the persistence-capable class.
Field numbers represent the position of a nonstatic field within the list of all nonstatic fields defined for the class and its superclasses. The first field has field number 1. (Note that the first field number is not 0.)
public Field[] getFields() { return fields; }
private static Field[] fields = {
Field.createString("name"),
Field.createInt("age"),
Field.createClassArray("children",
"COM.odi.demo.people.Person", 1)
};
The definition above causes PSE/PSE Pro to associate 1 with the name field, 2 with the age field, and 3 with the children field. When you define the initializeContents() and flushContents() methods, you must specify the correct field number for each field that the methods get and set.
Updated: 05/13/97 12:19:57