Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Dealing with Java Persistence


Dr. Dobb's Sourcebook September/October 1997: Dealing with Java Persistence

Johnny is the president of the Object Guild. He can be contacted at [email protected]


With the release of Java Developer's Kit (JDK) 1.1 and new Java standards for data storage, application developers face several alternatives for accessing persistent data -- relational databases are supported by JDBC, flat files can store streamed Java objects, and several vendors of object database management systems (ODBMS) are beginning to offer Java bindings to their storage engines (see Table 1).

The object database alternative offers three main benefits:

  • Transparency. The same mechanisms used to manipulate objects in memory are used for persistent objects, which means that the same object model used to design business application objects is used to define database schema, since the programming language is also used as the data definition language (DDL).
  • Graph structured data. Data in an object database is organized as a graph or network of interrelated data. This is similar to the way you program with objects and their references (pointers).
  • Smart caching. Data objects are retrieved from storage as needed. Typically when a reference is traversed, objects are automatically "faulted-in," without writing explicit code to fetch data from a database.

ODBMSs differ from their relational counterparts in that the underlying architecture of the ODBMS varies widely from vendor to vendor. Because of these architectural differences, different ODBMS products have benefits and drawbacks that depend upon the nature of your application.

Although C++ is well supported by object database vendors, not all vendors currently provide Java bindings to their databases, and those that do may not provide Java access to all of the features available from C++. This leaves programmers with a dilemma. Choosing an ODBMS tool may be based on Java binding features more than database architecture, and this choice might introduce application dependencies on one particular ODBMS product or version. It seems a shame to burden a well-designed application with an implementation that requires a particular data storage technique or that requires a particular data storage tool.

Leveraging JNI and C++ Object Database Bindings

For these reasons, my company (Object Guild) developed Direct Express, a tool that provides Java application developers with access to an object database's C++ binding without writing C++ code. Direct Express provides a straightforward mechanism for achieving Java persistence, which takes advantage of two standard offerings:

  • Java Native Interface (JNI). Javasoft's JNI, which was introduced with JDK 1.1, is the standard "glue" that is used to integrate Java and C++ in a platform-independent way.
  • C++ bindings. Object database vendors offer C++ bindings, which have been around for almost a decade and have many more proven deployments when compared to Java bindings. Furthermore, the ODMG provides a compliance suite for C++ bindings, whereas at the time of this writing, the ODMG has yet to release the official Java binding compliance suite.

Direct Express combines these two standard offerings by automating the generation of JNI and C++ code to provide reliable Java persistence through an object database's C++ binding.

Direct Express consists of three main components:

  • A translator generator (JGEN) that converts persistent-capable Java files into their concrete implementations.
  • Precanned translation definitions (.DEF files) used by JGEN.
  • A set of database transaction and connectivity classes, which support the ODMG Java interface specification.

Writing a Direct Express application involves identifying persistence-capable Java classes, then providing application control flow in order to coordinate connecting to databases and issuing commits at the appropriate transaction boundaries.

You first identify Java business application classes, which should be persistence capable. In accordance with the draft ODMG Java specification for object databases, persistence-capable Java classes are pure Java. Unlike the ODMG C++ binding, the Java binding does not use special syntax. It also does not require derivation from a base class to achieve persistence. With Direct Express, all that is necessary is that you process the persistence-capable classes that you have identified with the JGEN translator. In the examples that follow, notice that the keyword "transient" is used to define data members that are not stored persistently. (The ODMG has received Javasoft's blessings on the use of the "transient" keyword in persistent-capable classes.)

JGEN translates your Java classes into concrete Java and C++ files. There are two C++ files: a header file and its corresponding body. These define counterpart persistent C++ classes and define the JNI methods that link Java to C++. JGEN generates the C++ code automatically from translation definitions, and therefore, it is not necessary to write C++ code. The resulting Java code must be compiled into .class files and the resulting C++ code must be compiled and linked to produce a DLL. Figure 1 shows how JGEN translates a persistent-capable Java source file into three concrete implementation files: a Java source file containing JNI stubs; a C++ file containing JNI methods definitions; and a C++ header file, which is also the data definition language (DDL) file for defining object database schema.

Perhaps the most interesting aspect of Direct Express is that the JGEN translator generator offers a user-configurable approach to defining just what persistence means. JGEN reads from a definition file (which performs translation rewriting duties) to determine what files to generate and to determine what concrete code is emitted. The JGEN translation rewriting definitions use a tagged markup notation, which is very similar to SGML or HTML. Each tag identifies the target output file, the type of Java object under translation, and other information. The body of the tagged markup is the actual code with keyword substitution phrases.

JGEN's configurable definitions file lets you fine-tune the exact translations from Java to C++ and JNI, so that new versions of an object database's C++ binding can be accommodated without having to rewrite Java application code. This allows you to implement business or application objects independent of underlying persistence mechanisms, knowing that they will be supported by the translations provided by JGEN.

Listing One is an excerpt from a JGEN definition file, which prescribes the translation for Java String declarations into JNI code used to link Java to the Objectivity/C++ binding.

JGEN's configurable definitions file ensures that Java application code can work with new releases of C++ bindings, and provides flexibility in adapting to different persistence models or even different database products.

A Clinical Patient Record Example

To demonstrate a JGEN translation of a user's Java class definition for clinical patient records, I'll present a clinical patient record example. The Java class is named Patient and has been simplified for presentation.

Listing Two is the input Java file, which will be made persistence capable. Notice the use of the "transient" keyword to identify the guiCount data member, which is not stored, but only used in Java at run time.

In this example, I have configured JGEN to generate C++ and JNI for Objectivity/C++ Version 3.8. Figure 2 shows the Direct Express Configuration tool, which is used to select the appropriate C++ binding. For Objectivity/C++ 3.8, JGEN generates three output files: a Java file, a CPP file, and a DDL file. The Java file looks similar to the input file except that persistent definitions are automatically commented out and replaced by get and set methods. The get and set methods are implemented so that they pass the buck to private native methods. And these private native methods use JNI to access the corresponding C++ counterpart objects.

In Listing Three, the Java input file does not use a base class for persistence. Objectivity/C++, on the other hand, requires that all persistent-capable C++ objects inherit directly or indirectly from the base class ooObj. The persistent base class requirement would be too restrictive in Java, which does not support multiple inheritance. The ooObj base class is hidden behind the JNI machinery used to achieve persistence, and the additional instance methods needed to support persistence in Direct Express are automatically generated by the JGEN translator, thus Direct Express conforms to the ODMG Java specification by not requiring a Java base class.

Listing Four consists of an excerpt from the generated .DDL file. It shows C++ accessor methods for the medicalID attribute. With Objectivity/C++, the DDL file serves as a C++ header, as well as the data definition language.

Listing Five consists of an excerpt from the generated C++ file excerpt, which shows the JNI macros. These macros are used to link up the Java native declarations to their concrete implementations in C++.

Object or Relational

Although Direct Express was designed for use with object databases, it could, in theory, be used to build a relational database mapping code interface that conforms to the ODMG Java specification. This could be a task for the adventurous, who are comfortable with JGEN's tagged markup. It would certainly be possible to create a .DEF file to map from the Java input file to JDBC for relational database access. Each rewriting rule would specify a concrete implementation for get and set methods using the appropriate SQL and JDBC to retrieve and store values, effectively insulating the object model in your application code from the relational mapping code. The detailed issues in designing effective relational mapping layers are beyond the scope of this article, and touch on the theoretical underpinnings of object and relational database architectures.

Keep in mind that relational databases are "set-theoretic" because data is organized in tables (or views), which are sets of tuples. Interrelationships among data are not stored in the database, but are calculated on the fly via "join operations" that intersect sets of tuples on matching key fields. A separate data modeling and access language (SQL) is needed. JDBC provides Java integration for connectivity to SQL, and the programmer must write application code that provides a mapping between Java objects and relational data. This set theoretic property makes relational data flexible in performing queries, but makes transparent integration difficult with an object programming language, such as Java.

Object databases (and to some extent, their ancient cousins, network databases) are "graph-theoretic," on the other hand, because data is organized as datum nodes with directed relationships (pointers). Since the "pointers" are stored along with the data, this "graph-theoretic" property can make arbitrary queries difficult, but makes the mapping from an object language to database trivial. The elimination of database mapping code is achieved completely through "transparent object access" from an object language, such as Java.

Conclusion

Since JGEN uses customizable translation rules, using Direct Express buys a lot of flexibility in choosing an underlying persistence mechanism for your Java application. You can configure Direct Express to work with different C++ and Java bindings. In fact, Direct Express ships with precanned .DEF files for several C++ binding versions and an alpha Java binding from Objectivity. It is also possible to configure Direct Express to map to JDBC accessing a relational database. Ultimately, Direct Express enforces a clean separation between application and database implementation, which should contribute to an application's flexibility and longevity.

DDJ

Listing One

<code fileSuffix=.cpp sect=3 kind=String>
/* Class:     %underScoredPackageName%_%className%
 * Method:    %methodname%
 * Signature: (I)%Ljava/lang/String%
 */
JNIEXPORT jstring JNICALL
Java_%underScoredPackageName%_%className%_%methodname%__I
  (JNIEnv * env, jobject obj, jint handle) {
  ooHandle(%className%) & %lowerCaseClassName% = 
                                        *(ooHandle(%className% )*) handle;
  return env->NewStringUTF(%lowerCaseClassName%->%name%());
}

Back to Article

Listing Two

package COM.objectGuild.directExpress;

class Patient {
  int medicalID;
  String name;
  String ssn;
  Chart myChart;
  transient int guiCount;
}

Back to Article

Listing Three

  /** DO NOT EDIT. This file is generated by Direct Express. */

package COM.objectGuild.directExpress;

class Patient {
  /*persistent int medicalID;*/
  /*persistent String name;*/
  /*persistent String ssn;*/
  /*persistent Chart myChart;*/
  int guiCount;

  /** Return the medicalID field of class Patient object
   */
  public int medicalID() throws OGDBException {
    return p_medicalID(counterpart());
  }

  /** Set the medicalID field of the Patient object to the specified int
   */
  public void medicalID(int inmedicalID) throws OGDBException {
    p_medicalID(counterpart(),inmedicalID);
  }

  /** Native method that creates the counterpart in C++.
   * @return Address of the handle to the C++ object.
   */
  private native int makeCxxCounterpart();

  /**
   * Private native methods for medicalID
   */
  private native int p_medicalID (int handle) throws OGDBException;
  private native void p_medicalID (int handle, int medicalID)
    throws OGDBException;

  ...

}

Back to Article

Listing Four

class Patient : public ooObj {
  private:
    int _medicalID;
  int medicalID() const {
    return _medicalID;
  }
  void medicalID (int medicalID) {
    _medicalID = medicalID;
  }

Back to Article

Listing Five

/* Class:     COM_objectGuild_directExpress_Patient
 * Method:    medicalID
 * Signature: (I)INT
 */
JNIEXPORT jint JNICALL
Java_COM_objectGuild_directExpress_Patient_medicalID__I
  (JNIEnv * env, jobject obj, jint handle) {
  ooHandle(Patient) & patient = *(ooHandle(Patient )*) handle;
  return patient->_medicalID();
}
/* Class:     COM_objectGuild_directExpress_Patient
 * Method:    medicalID
 * Signature: (II)INT
 */
JNIEXPORT void JNICALL
Java_COM_objectGuild_directExpress_Patient_medicalID__IINT
  (JNIEnv * env, jobject obj, jint handle, int j_medicalID) {
  ooHandle(Patient) & patient = *(ooHandle(Patient )*) handle;
  patient->medicalID(j_medicalID);
}

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.