A persistence-capable class is one whose instances can be made persistent and saved in an InfiniteGraph database. When you define a persistence-capable class, you must consider its position in the inheritance hierarchies of the application, the range of persistence behavior that the class should support publicly and privately, and which of its fields should be saved persistently. This document discusses the decisions you must make when defining a persistence-capable class and describes how to implement those decisions.

Understanding Persistence-Capable Classes

A persistence-capable class supports InfiniteGraph operations, allowing instances of the class to act both as normal Java runtime objects and as objects stored persistently in a InfiniteGraph federated database. An application that needs to save objects persistently must define a persistence-capable class for each kind of object to be saved.


Note: You should not use the underscore character (_) in the name of any package that contains a persistence-capable class.


When you define a persistence-capable class, you must provide its persistence behavior by inheriting from a persistence capable class (e.g. Inherit from BaseVertex or BaseEdge, or from some subclass of BaseVertex or BaseEdge.) As with any class, defining a persistence-capable class entails defining its fields and methods.

Making a Class Persistence Capable

To provide the capability for persistence you can define a class that inherits from BaseVertex or BaseEdge

Once you have defined a persistence-capable class, any subclass you define from it inherits its persistence behavior.

Example

In this example, Vehicle is a persistence-capable class whose superclass is BaseVertex. Truck is a persistence-capable class whose superclass is Vehicle.

[sourcecode language=”java”]
import com.objy.graph.BaseVertex;

// Make class persistence-capable by inheritance
public class Vehicle extends BaseVertex {

}

[/sourcecode]


[sourcecode language=”java”]
import com.objy.graph.BaseVertex;

// Make class persistence-capable by inheritance
public class Truck extends Vehicle {

}
[/sourcecode]


Defining Fields

Persistent objects can have persistent fields, whose values are saved in the InfiniteGraph federated database, and transient fields, whose values are not saved. Every persistent field represents either an attribute:

  • The attributes of a class constitute its persistent characteristics or state; they enable an application to set the values that define the features or status of each instance of the class.

Persistent Fields

All nonstatic and nonfinal fields you define for a persistence-capable class are persistent by default. When the object is written to the InfiniteGraph federated database, the values in those fields are saved persistently.


Every persistent field must be of one of the following data types.

Category Types
Numeric attributes char

byte

short

int

long

float

double

boolean

String attributes java.lang.String

java.lang.StringBuffer

Date or time attributes java.util.Date

java.sql.Date

java.sql.Time

java.sql.Timestamp

Any field that is not of one of these data types must be declared as transient. See Transient Fields.


Note: If a persistent object has a field for a date, a time, or an array of dates, times, or strings, the value for that field is actually a reference to an internal persistent object representing the data, time, or array data.


If your Java application will interoperate with applications written in C++, C#, or Smalltalk, you must select field types that will map to InfiniteGraph data types that are supported by the other languages. Similarly, if you are defining a Java class corresponding to an existing class description in the schema, you must select field types that will map to the InfiniteGraph attribute and relationship types in the schema description. For more information on this topic, see Objectivity documentation on Schema Matching for Interoperability

Example

This example illustrates the persistent fields that represent attributes

[sourcecode language=”java”]
public class Vehicle extends BaseEdge {
// Attribute fields
protected String license;
protected String type;
protected int doors;
protected int transmission;
protected boolean available;

}
[/sourcecode]


As the preceding table indicates, you should not define persistent fields that reference the database and InfiniteGraph federated database where an object is stored, the transaction to which the object belongs, or the object’s identifier.You can, however, obtain this information through methods defined on BaseVertex or BaseEdge.


Transient Fields

A persistence-capable class can have transient fields, whose values are not saved when an object is written to the InfiniteGraph federated 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’s data is fetched from the InfiniteGraph federated database. Transient field are not copied when a persistent object is copied. You must set the value of a transient field explicitly.

You must define a field as transient in either of the following circumstances:

  • The field’s declared type is not one of the supported types for persistent fields.
  • The field’s declared type is an interface and you intend to reference transient objects in that field.

Example

This example illustrates a transient field dailyRate

[sourcecode language=”java”]
public class Vehicle extends ooObj {

// Transient field
protected transient int dailyRate;

}
[/sourcecode]


Defining Access Methods

When you retrieve a persistent object, you obtain an empty object. You need to fetch the object’s data before you can access the object’s attributes. The methods that fetch data also lock the object if it is not currently locked–for example, because the transaction in which you retrieved the object has committed and therefore released the lock. You can ensure that objects of your class are used safely by accessing attributes 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 reference attribute 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 need to provide a conversion application to convert objects in the InfiniteGraph federated database from the old implementation to the new implementation. See Objecivity documentation on Schema Evolution and Object Conversion.

If your persistence-capable class extends BaseVertex or BaseEdge, your access methods can call the fetch and markModified methods of the persistent object.

Attribute Access Methods

You should define attribute access methods for every attribute of a class and call those methods whenever you get or set the value of an attribute.

  • It is not safe to access the attributes of a persistent object before the object’s container has been locked for read and its persistent data has been fetched. To ensure that objects of your class are used safely, the access method that gets the value of an attribute should call the fetch method of the object (or its persistor) before returning the attribute’s value.
  • Attributes of an object should be modified only if the object’s container is locked for write and the object’s data has been fetched. If changes are made, the object must be marked as modified so that the changes are written to the federated database. To ensure that your objects are modified safely and that changes are written to the InfiniteGraph federated database, the access method that sets the value of an attribute should call the markModified method of the object (or its persistor) before changing the value.

Note: Any access method that calls the fetch or markModified method of a persistent object must be called within a transaction.
While the object is transient, fetch or markModified have no effect, so they can be called outside a transaction.


Single-Valued Attributes

If an attribute has a single value (as opposed to an array of values), you can define one access method to get the value in the field. If the attribute’s value can be changed, you can define another access method to set the value.

Example

This example illustrates some attribute access methods for the Vehicle class. Each attribute has an access method that gets the value of that attribute; the getLicense method illustrates the form of these methods. All other attributes are initialized when a vehicle object is created; only the available attributes can be modified after initialization. The attribute access methods rentVehicle and returnVehicle set the Boolean attributes available to false and true, respectively.

[sourcecode language=”java”]
// Attribute access methods to get attribute values
public String getLicense() {
fetch();
return this.license;
}

public void rentVehicle() {           // Set available attribute
markModified();
this.available = false;
}

public void returnVehicle() {        // Set available attribute
markModified();
this.available = true;
}
[/sourcecode]


Array Attributes

If your class has array attributes, 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.

Example

This example illustrates access methods for an array attribute.
The getInt method gets the value at a particular array index and the setInt method sets the value at a particular index.

[sourcecode language=”java”]
public int getInt(int n) {
fetch();
return this.myIntArray[n];
}

public void setInt(int n, int newInt) {
markModified();
this.myIntArray[n] = newInt;
}
[/sourcecode]

An alternative approach to accessing array fields directly is to hide the implementation with attribute access methods that manage the array.

Defining Application-Required Methods

You can define any methods for your persistence-capable classes that your applications require. Note, however, that InfiniteGraph 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 attributes and relationships and implementations for its application-defined methods.


Note: If you define a finalize method for a persistence-capable class, you must not perform any InfiniteGraph operation in that method. Different implementations of the Java virtual machine may execute finalize methods in a separate thread. If a finalize method running in its own thread accesses a persistent object whose session enforces the restricted thread policy, a NotJoinedException is thrown.


SHARE THIS POST
Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedIn