Build an object database, Part 2: Object storage
backend - JavaWorld April 2000
Tutorial Details:
Build an object database, Part 2: Object storage backend
Build an object database, Part 2: Object storage backend
By: By Michael Shoffner and Merlin Hughes
Implement relational database storage for Java objects
apping objects into relational databases and vice versa is almost always a hassle. A general solution is difficult because of the inherent differences in the relational and object-oriented approaches to data modeling.
Tools do exist to help with this process, usually at a "generous fee" (with respects to the late Curtis Mayfield). Sometimes the tools are necessary, especially when mapping from an existing relational data model to an object model. However, when the relational model is not set in stone, it is possible to let the object model drive the relational model, allowing storage options to surface closer to home.
Build an object database: Read the whole series!
Part 1: Construct a frontend to translate between Java objects and relational database records
Part 2: Implement relational database storage for Java objects
The most obvious solution: serialize each Java object using the object streams and slap the result into a database as a binary blob. While this is certainly a valid option and the JDBC explicitly supports it, blobs cannot be readily manipulated (or even read) by anything other than other Java applications. So interoperability, along with human readability, go out the door.
The relational storage backend we'll build alleviates this data- interoperability problem. Our backend actually creates relational tables for each class and maps each instance's variables into them as columns, creating a sort of poor man's object-relational mapping, if you will. (To download this article's complete source code, go to Resources .)
Framework overview revisited
The object-storing framework introduced in the January Java Step by Step time divides the work of persisting Java objects into two tasks:
Frontend: Scatter the object into its fields, preserving its type information -- the responsibility of an ObjectStorer implementation.
Backend: Store the values of the fields along with type information -- the responsibility of an ObjectStorage implementation.
Figure 1. The object storage architecture
The object-storing framework enables object storers and object storage implementations to vary independently. The object storer doesn't care how the object storage implements its storage behavior, and the object storage doesn't know that the object storer exists. This is as it should be.
The ObjectStorer interfaces look like this:
public interface ObjectStorer {
public void put (Object key, Object object) throws IOException;
public Object get (Object key) throws IOException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException;
}
In the January Java Step by Step , Merlin provides implementations of this interface -- most notably a SerializationStorer .
In this article, we'll complete the framework by implementing the ObjectStorage interface, as seen below:
public interface ObjectStorage {
public void put (Object key, StorageFields object) throws IOException;
public RetrievalFields get (Object key) throws IOException;
}
The ObjectStorage interface uses the StorageFields and the RetrievalFields classes to pass information about fields that are stored and retrieved, as we'll see next.
Class StorageFields
As you'll no doubt recall, the StorageFields is a collection of the scattered fields, as well as the stored Java object's type information. The ObjectStorer implementation hands an instance of this class to the ObjectStorage implementation as a parameter to the put() method.
Class RetrievalFields
The RetrievalFields class is a collection of fields returned by the ObjectStorage implementation in response to a get() call. It is practically the same as the StorageFields class, except that there is no need to explicitly state type information since it is represented by the class of the object being returned.
We'll call our ObjectStorage implementation class SQLObjectStorage .
SQLObjectStorage's data model and constraints
Before we delve into the code, let's outline how our SQLObjectStorage 's relational data model will work.
Figure 2. SQLObjectStorage's data model
The main concept behind this extremely simple data model is: SQLObjectStorage creates a relational table for each class to be stored in the database. SQLObjectStorage then stores instances of a given class in the table created to correspond to that class.
Each table includes a set of columns that correspond to the fields defined in the class. SQLObjectStorage dynamically creates tables and columns using a database-specific mapping from Java types to column types supported by the database.
A bit of overhead information -- the mapping from each key to its respective instance in the database -- is kept in the database in addition to the stored instances. To achieve this end, SQLObjectStorage creates a key column in each table to key the instances therein. It also creates a special key table in the database to store every key and the table where it resides.
In addition, SQLObjectStorage imposes the following constraints:
Each database's keys are unique: A key uniquely identifies an object in a specific database. If a key/value pair exists in the database and another value is entered with the same key, the new value replaces the old value.
There are no duplicate entries per key: Each key will store against only one value.
One table exists per primary class type (classname): Each class is stored as an instance of its primary class, with all of its fields as well as superclass fields flattened out into the columns of the table that bears its name.
Class and field names are escaped: In order to avoid illegal database characters, encode class names with escape characters to get table names. The same process applies to mapping fields to column names.
Values are escaped: Field values are escaped to avoid problems with SQL statement syntax.
Abstract class SQLObjectStorage
So now we come to the ObjectStorage implementation. We'll look at just the important methods here. (For the complete source code, see Resources .)
First, we look at the ObjectStorage -interface method implementations, starting with put() .
The put() implementation takes a key and a StorageFields object as arguments. It then removes any existing entries in the database under the key. If there is an object to store, it creates a table and stores the object's field values to it as follows:
public synchronized void put (Object key, StorageFields object) throws
IOException {
if (key == null) return;
if (DEBUG) System.out.println (this + "::put: key is '" + key + "', object
is " + object);
if (DEBUG_2) System.out.println (object);
String keyString = key.toString ();
try {
removeEntries (keyString); // always remove old entries under key, if
they exist
if (object != null) {
createTable (object);
storeValuesToTable (keyString, object);
}
} catch (SQLException ex) {
if (DEBUG) {
System.err.println (this + "::put: caught exception... ");
System.err.println ("SQLException: " + ex.getMessage ());
System.err.println ("SQLState: " + ex.getSQLState ());
System.err.println ("VendorError: " + ex.getErrorCode ());
}
throw new IOException (ex.getMessage ());
}
}
Next, the get() method returns a RetrievalFields . The first step is to get the class table for the key. Then the get() method obtains a RetrievalFields for the key in the correct class table:
public synchronized RetrievalFields get (Object key) throws IOException {
if (key == null) return null;
if (DEBUG) System.out.println (this + "::get: key is '" + key + "'");
RetrievalFields fields = null;
try {
String keyString = key.toString ();
String classTable = getClassTableForKey (keyString);
fields = getEntryForKeyInTable (keyString, classTable);
} catch (SQLException ex) {
if (DEBUG) {
System.err.println (this + "::get: caught exception... ");
System.err.println ("SQLException: " + ex.getMessage ());
System.err.println ("SQLState: " + ex.getSQLState ());
System.err.println ("VendorError: " + ex.getErrorCode ());
}
throw new IOException (ex.getMessage ());
}
return fields;
}
Moving on, the getConnection() , getTypeString() , and getObjectFromResultSet() methods are deferred to subclasses. The getConnection() method knows how to make a connection to the specific database a subclass represents. getTypeString() and getObjectFromResultSet() mapped type information from the database column types to Java types and vice versa:
// subclasses define driver-specific connection tasks and set the connection
variable
protected abstract void getConnection () throws SQLException;
// subclasses define database-specific type mapping
protected abstract String getTypeString (Class type);
// subclasses define type information when reconstructing objects and
sqlDecode () strings
protected abstract Object getObjectFromResultSet (ResultSet rs,
int colNum,
String colName,
int jdbcType) throws
SQLException;
Next is an example of the flavor of the methods that SQLObjectStorage uses to store objects in a generic SQL database:
protected void createTable (StorageFields object) throws SQLException {
// CREATE TABLE TABLE_NAME (COL1_NAME COL1_TYPE, COL2_NAME COL2_TYPE, ...);
Statement statement = null;
try {
StringBuffer sqlBuffer = new StringBuffer ();
String tableName = sqlEncode (object.getClassName ()); // to get rid of .
in classname
if (doesTableExist (tableName)) return;
sqlBuffer.append ("CREATE TABLE " +
tableName + " (" +
KEY_COLUMN_NAME +
" VARCHAR(" +
getMaxKeyLength () +
"), ");
Iterator fieldNames = object.getFieldNames ();
while (fieldNames.hasNext ()) {
String fieldName = (String) fieldNames.next ();
Class type = object.getType (fieldName);
String typeString = getTypeString (type);
sqlBuffer.append (sqlEncode (fieldName) + " " + typeString
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Build an object database, Part 2: Object storage
backend - JavaWorld April 2000
View Tutorial: Build an object database, Part 2: Object storage
backend - JavaWorld April 2000
Related
Tutorials:
Sir, what is your preference?
Sir, what is your preference? |
Implement a J2EE-aware application console in Swing
Implement a J2EE-aware application console in Swing |
I want my AOP!, Part 1
I want my AOP!, Part 1 |
Use Web services
to integrate Web applications with
EISs
Use Web services
to integrate Web applications with
EISs |
Step into
the J2EE architecture and process
Step into
the J2EE architecture and process |
Ilog JRules 4.0:
Working by the rules
Ilog JRules 4.0:
Working by the rules |
Create your own type 3 JDBC driver, Part 2
Create your own type 3 JDBC driver, Part 2 |
Good
introduction to JDO
Good
introduction to JDO |
Should you go
with JMS?
Should you go
with JMS? |
J2SE 1.4
breathes new life into the CORBA community, Part
3
J2SE 1.4
breathes new life into the CORBA community, Part
3 |
High-availability mobile applications
High-availability mobile applications |
Finally, getting hands in !
Finally, getting hands in ! |
Smart Value Object goes
one step further
Smart Value Object goes one step further
The Smart Value Object allows server components to track client-side modification of business objects in a rich client/J2EE server environment, by using the latest features offered by bytecode processing tools.
|
Maybe the future UI design of choice
Maybe the future UI design of choice |
Extensible Code Generation with Java, Part 1
Extensible Code Generation with Java, Part 1
Code generation is a key new trend in engineering, one that you need to understand well. The reason is simple: today's modern frameworks are extremely code-intensive. Using a code generator to build the code |
Simple classes for JDBC
Simple classes for JDBC |
Handling Events in JavaServer Faces, Part 2
Here in part two, Hans implements event handling for parts of the sample application discussed in part one.
|
Accessing the Database from Servlet
This article shows you how to access database from servlets. Here I am assuming that you are using win95/98/2000 and running Java Web Server. |
Apache Directory Project
ApacheDS is an LDAP and X.500 experimentation platform. Its backend subsystem and frontend are separable and independently embeddable. It provides a server side JNDI LDAP provider that directly interacts with the backend storage. It is powered by SEDA (St |
What is Persistence Framework?
What is Persistence Framework?
What is Persistence Framework?
A persistence framework moves the program data in its most natural form (in memory objects) to and from a permanent data store the database. The persistence framework manages the |
|
|
|