org.garret.consus
Class JDBCObjectRelationalAdapter

java.lang.Object
  extended by org.garret.consus.JDBCObjectRelationalAdapter
All Implemented Interfaces:
PersistentObjectStorage

public class JDBCObjectRelationalAdapter
extends java.lang.Object
implements PersistentObjectStorage

This class implements the Consus OO API on top of standard relational database systems. The tables used by applications should be explicitly created by the programmer using the standard JDBC protocol. This is the main difference with implementation of PersistentObjectStorage by ConsusConnection. In the last case, tables are automatically generated from class definitions. Below is the initialization sequence for a database to be used with JDBCRerlationalAdapter

  1. Create TypeInfo table with TID, TableName, ClassName fields.
  2. Insert in this table the information about all the tables/classes used by the application. This table is assigned a unique id to the type and establishes mapping between the application class and database table.
  3. Create OidTable. This table contains a single column ID with a single row and is used to generate new object identifiers.
  4. Insert initial value of identifier into the OidTable table.
  5. Create tables for all classes used by the application. For reference fields you should choose the database type which will be able to hold a Java long value.
All arrays should be stored as a VARBINARY type. Non-persistent object fields should be represented by their components concatenated with the JDBCObjectRelationalAdapter.STRUCTURE_FIELDS_SEP character (by default $). Mapping of other Java types to native RDBMS types is up to the programmer. The only requirement is that JDBC driver will be able to extract/store fields of that object, using getXXX and putXXX methods of java.sql.ResultSet and java.sql.PreparedStatament, where XXX is the Java type of the field. Each table should have RID and TID fields which are used to store record and type identifiers. Both identifiers are of the Java int type. The Object identifier (OID) is constructed by concatenation of type and record identifiers.

To check whether the database was initialized or not, the programmer should use the PersistentObjectStorage.isInitialized() method. It should be called before opening and so the database should be initialized prior to invocation of the PersistetnObjectStorage.open() method.

These rules are illustrated by the following example:

Application classes:

 class Address { 
     String  country;
     String  city;
     String  street;
 };
 
 class Person extends Persistent { 
     String   name;
     Address  address;
     Person   mother;
     Person   father;
     Person[] childs; 
 };
 
 Connection con = DriverManager.getConnection(databaseURL);	
 PersistentObjectStorage storage = (con instanceof PersistentObjectStorage) 
     ? (PersistentObjectStorage)con : new JDBCObjectRelationalAdapter(con);
 if (!storage.isInitialized()) {
     Statement stmt = con.createStatement();
     stmt.executeUpdate("create table TypeInfo (TID int, TableName varchar, ClassName varchar)");
     stmt.executeUpdate("create table OidTable (ID int)");
     stmt.executeUpdate("insert into OidTable values (1)");
     stmt.executeUpdate("create table Person (name varchar primary key," + 
                                             "address$country varchar," + 
                                             "address$city varchar," + 
                                             "address$street varchar," + 
                                             "mother bigint," + 
                                             "father bigint," + 
                                             "childs varbinary)");
     stmt.executeUpdate("insert into TypeInfo values (1, 'Person', 'Person')");
 }
 storage.open();
 
Underlying RDBMS should have
  1. TypeInfo table with TID, TableName and ClassName columns, which specifies the type mapping between Java classes and RDBMS table.
  2. OidTable with a single row in the single OID column, which is used for assigning OIDs to the inserted object.
  3. OID and TID columns in each table, which store object and table identifiers


Field Summary
static java.lang.String OBJECT_RID_NAME
           
static java.lang.String OBJECT_TID_NAME
           
static java.lang.String OID_TABLE_COLUMN_NAME
           
static java.lang.String OID_TABLE_NAME
           
static java.lang.String STRUCTURE_FIELDS_SEP
           
static java.lang.String TYPE_CLASS_NAME_FIELD
           
static java.lang.String TYPE_ID_FIELD
           
static java.lang.String TYPE_TABLE_NAME
           
static java.lang.String TYPE_TABLE_NAME_FIELD
           
 
Constructor Summary
JDBCObjectRelationalAdapter(java.sql.Connection con)
          Constructor of the adapter class
 
Method Summary
 void becomeObject(Persistent a, Persistent b)
          Exchange references to the objects in the database.
 void clearComponents(Persistent obj)
          Clear all object fields (to make it possible for GC to collect unused persistent objects).
 void clearObjectCache()
          Throw all objects from the object cache.
 void deleteObject(Persistent obj)
          Remove object from the database.
 java.lang.Object fetchObject(java.sql.ResultSet cursor)
          Fetch the current row of the cursor as a Java object
 java.lang.Object getObjectByOid(long oid)
          Get object by object identifier.
 int getObjectCacheSize()
          Poll object cache size
 TableIterator getTableIterator(java.lang.String tableName)
          Get the iterator for the table.
 long insertObject(java.lang.Object obj)
          Insert a new object into the database.
 boolean isInitialized()
          Returns true if the database has already been initialized.
 void loadComponents(Persistent obj)
          Loads the components of the object.
 void loadObject(Persistent obj)
          Load stub object or reload object from the database.
 void lock()
          Lock the database in exclusive mode.
 void open()
          Opens the persistent object storage.
 void storeObject(Persistent obj)
          Save object into the database.
 void updateObject(java.lang.Object obj, java.sql.ResultSet cursor)
          Fetch the current row of the cursor as a Java object
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

OBJECT_RID_NAME

public static java.lang.String OBJECT_RID_NAME

OBJECT_TID_NAME

public static java.lang.String OBJECT_TID_NAME

OID_TABLE_COLUMN_NAME

public static java.lang.String OID_TABLE_COLUMN_NAME

OID_TABLE_NAME

public static java.lang.String OID_TABLE_NAME

STRUCTURE_FIELDS_SEP

public static java.lang.String STRUCTURE_FIELDS_SEP

TYPE_CLASS_NAME_FIELD

public static java.lang.String TYPE_CLASS_NAME_FIELD

TYPE_ID_FIELD

public static java.lang.String TYPE_ID_FIELD

TYPE_TABLE_NAME

public static java.lang.String TYPE_TABLE_NAME

TYPE_TABLE_NAME_FIELD

public static java.lang.String TYPE_TABLE_NAME_FIELD
Constructor Detail

JDBCObjectRelationalAdapter

public JDBCObjectRelationalAdapter(java.sql.Connection con)
Constructor of the adapter class

Parameters:
con - - opened JDBC connection
Method Detail

becomeObject

public void becomeObject(Persistent a,
                         Persistent b)
                  throws java.sql.SQLException
Exchange references to the objects in the database. The result of execution A.becomeObject(B) is that OIDs of A and B are exchanged, so all references to A in the database will now refer to B, and vice versa. This operation doesn't affect references in main memory. Object A should already have an assigned OID (being stored in the database). If B is not yet stored in the database, it is explicitly assigned an OID by this method.

Specified by:
becomeObject in interface PersistentObjectStorage
Parameters:
a - object which OID will be exchanged with b object
b - object which OID will be exchanged with a object
Throws:
java.sql.SQLException

clearComponents

public void clearComponents(Persistent obj)
                     throws java.lang.Exception
Clear all object fields (to make it possible for GC to collect unused persistent objects). If this method is not called and all persistent objects in the database are referencing each other (for example linked in a L2-list), then after some time all the data can be loaded into memory from the database.

Specified by:
clearComponents in interface PersistentObjectStorage
Parameters:
obj - persitent object which components are cleared
Throws:
java.lang.Exception

clearObjectCache

public void clearObjectCache()
Description copied from interface: PersistentObjectStorage
Throw all objects from the object cache. This method should be called to prevent memory exhaustion caused by loading a lot of persistent interconnected objects (each of the objects is accessible from some set of root objects) from the database. GC will not be able to deallocate such objects because they are referencing each other. So finally all object from the database can be loaded to the memory. Programmer should check number of loaded objects (by getObjectCacheSize or getUsedMemorySize method) and if it axceeds some threshold, call clearObjectCache method to remove all objects from cache. Alternativly, progrtammer can call Persistent.unget method to replace object with a stub, but this approach is less efficient because usually it throws away most recently used object.

Specified by:
clearObjectCache in interface PersistentObjectStorage

deleteObject

public void deleteObject(Persistent obj)
                  throws java.sql.SQLException
Remove object from the database.

Specified by:
deleteObject in interface PersistentObjectStorage
Parameters:
obj - deleted persitent object
Throws:
java.sql.SQLException

fetchObject

public java.lang.Object fetchObject(java.sql.ResultSet cursor)
                             throws java.lang.Exception
Fetch the current row of the cursor as a Java object

Specified by:
fetchObject in interface PersistentObjectStorage
Parameters:
cursor - - result set with the current position corresponding to the fetched object
Returns:
fetched object
Throws:
java.lang.Exception

getObjectByOid

public java.lang.Object getObjectByOid(long oid)
                                throws java.lang.Exception
Description copied from interface: PersistentObjectStorage
Get object by object identifier.

Specified by:
getObjectByOid in interface PersistentObjectStorage
Parameters:
oid - - identifier of object to be fetched
Returns:
object with specified OID
Throws:
java.lang.Exception

getObjectCacheSize

public int getObjectCacheSize()
Description copied from interface: PersistentObjectStorage
Poll object cache size

Specified by:
getObjectCacheSize in interface PersistentObjectStorage
Returns:
number of object in object cache.

getTableIterator

public TableIterator getTableIterator(java.lang.String tableName)
                               throws java.sql.SQLException
Get the iterator for the table. This is a more efficient way for iteration through the the table then using "select * from" and ResultSet.

Specified by:
getTableIterator in interface PersistentObjectStorage
Parameters:
tableName - name of th table through which iteration will be performed
Returns:
table iterator for specified table
Throws:
java.sql.SQLException

insertObject

public long insertObject(java.lang.Object obj)
                  throws java.lang.Exception
Insert a new object into the database.

Returns:
reference to the created object
Throws:
java.lang.Exception

isInitialized

public boolean isInitialized()
                      throws java.lang.Exception
Returns true if the database has already been initialized. If database was not initilialized, the programmer has to create the TypeInfo, OidTable and application tables, insert the information about class-table mapping into the TypeInfo table and initialize the ID counter by inserting the value into OidTable. Initialization can be done through the standard JDBC methods.

Specified by:
isInitialized in interface PersistentObjectStorage
Returns:
true if database was already initialized, false otherwise
Throws:
java.lang.Exception

loadComponents

public void loadComponents(Persistent obj)
                    throws java.lang.Exception
Loads the components of the object. This method should be called for the classes which disable implicit load of object closure, that is the other objects referenced by the object being loaded. In this case referenced components should be loaded explicitly by this method.

Specified by:
loadComponents in interface PersistentObjectStorage
Parameters:
obj - persistent object which components are loaded
Throws:
java.lang.Exception

loadObject

public void loadObject(Persistent obj)
                throws java.lang.Exception
Load stub object or reload object from the database.

Specified by:
loadObject in interface PersistentObjectStorage
Parameters:
obj - loaded persistent object
Throws:
java.lang.Exception

lock

public void lock()
Lock the database in exclusive mode. The lock is kept until the end of transaction. This method should be called to prevent deadlocks caused by upgrading shared locks to exclusive. For example, if you first issue a read-only statement and then, within the same transaction issue an update statement, it can cause deadlock if concurrent thread also tries to upgrade the shared lock to exclusive.

Specified by:
lock in interface PersistentObjectStorage

open

public void open()
          throws java.lang.Exception
Opens the persistent object storage. This method should be called after checking that storage is initialized. If it has not been initialized then it should be initialized.

Specified by:
open in interface PersistentObjectStorage
Throws:
java.lang.Exception

storeObject

public void storeObject(Persistent obj)
                 throws java.lang.Exception
Save object into the database. If the object refers to some some other persistent-capable objects without assigned object identifiers, then these objects will also be recursively saved into the database.

Specified by:
storeObject in interface PersistentObjectStorage
Parameters:
obj - stored persitent object
Throws:
java.lang.Exception

updateObject

public void updateObject(java.lang.Object obj,
                         java.sql.ResultSet cursor)
                  throws java.lang.Exception
Fetch the current row of the cursor as a Java object

Specified by:
updateObject in interface PersistentObjectStorage
Parameters:
obj - - object with the new values
cursor - - result set with the current position pointing to the updated row
Throws:
java.lang.Exception