Object Persistence
Using Reflection
Use the Java Reflection APIs to achieve persistence and get better data access design.
by Ganesh Ghag
Posted August 13, 2002
The problem of persistence has long dogged object-oriented developers working with databases. But not everyone can afford the luxury of lavish object-oriented databases. And if you can't be rich, you'd better be clever.
Luckily, there's a poor man's solution to this problem of saving objects to a persistent mediumfor example, a relational databaseand loading data back from the medium into object instances.
It's not even difficult to do. First, we'll write an abstract base class for saving objects to a relational database. Next, we'll show how to access a derived class's data members from this "abstract base class," which is easy to do by using the Java Reflection APIs.
The beauty of this approach is that the abstract base class acknowledges more than just the data members, methods, and other vital information of the derived class. It can actually know the values of the data members of the derived class, at any given instant, during program execution.
Normally, derived classes need to implement methods defined in the abstract base class. But see how we've treated abstract protected Object registerSelfWithBaseClass() in the following example:
public class CPersistent { public CPersistent() { mDerivedClass = registerSelfWithBaseClass(); mDerivedClassName = mDerivedClass.getClass().getName(); } abstract protected Object registerSelfWithBaseClass(); //private data members protected String mDerivedClassName; protected Object mDerivedClass; }
Using this method, the base class can get a reference to the user's derived class. It can then discover important details about the derived class. For example, the base class can learn all of the following about the user's class:
A method in the base class can also be made to print out all information about the derived class, as in the following code:
public void printClassFieldsInfo() throws ClassNotFoundException, IllegalAccessException { Class c = Class.forName(mDerivedClassName); System.out.println("from CPersistent base class!!"); System.out.println(c.getName()); Field[] fields = c.getDeclaredFields(); for(int i = 0 ; i < fields.length ; i++) { System.out.println("field: "+i); System.out.println(fields[i].getName()); System.out.println(fields[i].getType()); System.out.println(fields[i].getModifiers()); System.out.println("value:" + fields[i].get(mDerivedClass)); } }
Now that we have a way to access a user's derived class's information in an abstract base class, we can enhance this abstract base class to save or load the values specified in the data members of the derived class to or from "suitable" RDBMS tables.
Database operations rely heavily on four basic operations: insert, update, delete, and load. These implementations can now be performed using the abstract base class, depending on your persistence strategy.
In addition to these basics, each derived class needs to specify the tables and columns to which its data members should be mapped. One simple mechanism for this is to generate a <derived_class_name>.properties file in this format:
<table_name>.<column_name>=<data_member_name>
Next, have the base class load this file and read the RDBMS-to-object mapping. Using this mapping, the base class implementation can formulate appropriate RDBMS queries (using Java's SQL APIs), which can then be invoked to insert, update, delete or load data into and from RDBMS tables.
This approach can be further enhanced by using class DatabaseMetaData to enforce checks such as those for primary keys. It also will provide meaningful exception messages to developers. And of course, advanced features such as referential integrity can be built up with a bit more effort.
This mechanism provides a flexible, easy, and inexpensive way to save object data to persistent media. It also decouples the Java object code from the persistence mechanism. Using this approach, the base class's persistence-implementation strategy can be changed radically without affecting the derived classes at all. Both of these are big design wins, and are fairly easy to achieve with a few simple tricks.
Of course, "rolling your own" is not for everyone, and you might choose to use off-the-shelf products that solve the problem of persistence. The following are good resources where you can explore a few products:
The Java Community Process Program's Java Specification Request JSR-12 (Java Data Objects Specification) describes a complete specification for a standard API for interface-based definitions of data stores and transactions, and selection and transformation of persistent storage data into native Java programming language objects. Sun provides a reference implementation of this specification as a downloadable product, Java Data Objects Reference Implementation, 1.0.
Project Castor's Castor JDO is a readymade product that can be used for convenient Java Object persistence to RDBMS.
Project Jakarta's Object Relational Bridge (OJB) allows seamless persistence of Java objects to relational databases.
About the Author
Ganesh Ghag is a senior software project lead whose work includes consulting at CitiGroup's Wall Street group. He has more than seven years of experience in software development Contact Ganesh at ganesh.ghag@citigroup.com