Persistent Objects in Java Code
Status: Alpha
Brought to you by:
guenne
Persistent Objects for Java =========================== License: GNU Lesser General Public License Requirements ------------ - Java version 1.5 or newer - GNU Getopt for Java (included -- see lib/README) - JDBC driver Additional requirements for the PObjForm/PObjSearch library ----------------------------------------------------------- - Servlet Java Classes Additional requirements for compilation --------------------------------------- - Apache Ant 1.6 or newer - Checkstyle (included -- see lib/README) Optional requirements --------------------- - Apache DBCP (for de.mguennewig.pobjects.optional.BasicContainerPool) Table of Contents ================= 1. Introduction 2. Basic Concepts 3. Examples 4. Package Overview 5. Compiling from Source 6. Thread-safety and concurrency 7. References 1. Introduction =============== Persistent Objects for Java is a framework for storing object data in a RDBMS. Or, if one starts with a database, a framework that presents relational data as a set of objects. It consists of a thin library ("thin" because aspects of RDBMS shine through in parts) and a code generator that provides the glue code bridging the gap from internal to external data representation. For the most part, the "glue" are get and set methods on otherwise hidden record fields within classes representing table rows. A number of basic assumptions guide the design and implementation of the persistence layer: * The goal is to make the use of persistent objects in a program as convenient as possible. Developers should be able to operate on persistent objects similar to normal objects. * Referential integrity between table rows is preserved in memory. That is, all foreign key references to a particular row in the relational database are mapped to pointers to a single object in memory, and vice versa. * Access is provided to table data, views, and arbitrary selects. The API provides a similar interface for all of these variants. A subset of SQL is made available to the application as a simple query language. Depending on the type of application, the scope of this subset is powerful enough to cover a large part of the queries it needs to run. If an application manipulates symbolic data, then all of its queries can typically formulated in this simple language. On the other hand, for an application including number crunching, the aggregation of data must be done with manually crafted SQL selects. * The framework offers sufficient flexibility to tune the tradeoff between convenience and performance. For example, the running time of most applications is determined by the number of interactions between client program and database server. Accordingly, the library offers ability to change from fine grained access to single objects to coarse grained data access internalizing thousands of objects with a single `select' statement. * PObjects does not provide an object oriented view on *any* SQL database schema. The persistence layer supports a limited number of mappings between relational data and objects. * For the persistence mechanism to work, classes must be explicitly programmed for it. That is, the library is not a mechanism that magically allows to store existing classes in a database. Instead, the persistence capable classes must be derived from a common base class, `de.mguennewig.pobjects.Record'. * Not all aspects of persistence are hidden from the developer. Persistent objects differ in some details from normal objects. For example, concurrency is not an issue if an object is under the full control of a program, but becomes very important if object data is serialized in a database, where several applications and users may access it simultaneously. The persistence layer exposes details of the lower level database access if these details cannot be abstracted away without loss of control. Most notably, the application must deal with transactions. The project is based on a port I made of the `libpobj' library that is part of the `Optimizing Oberon-2 Compiler' [5]. 2. Basic Concepts ================= Three basic concepts summarize the operation of the persistent object abstraction layer: 1. Every table corresponds to a class. 2. Every table row corresponds to an instance of the table's class. 3. Every attribute in a table row corresponds to a member of the table's class. As far as the application is concerned, the database is a container of objects * from which it can retrieve objects that were made persistent earlier, * where it can create new objects or can make changes to objects persistent, and * which it can query for lists of objects satisfying a given set of criteria. The authoritative source of persistent data is always the database. In general, it must be assumed that multiple processes are reading and writing to the same database at the same time. This means, that all objects and their state held locally be the application are only copies of the original data, and may become out of date immediately after they were taken from the database. (This is another aspect of a shared database that is _not_ abstracted away by PObjects, although some features of its implementation try to lessen the impact of this fact.) As far as possible, data items from the database are presented to the application in a format suitable for easy access by the programming language. For example, a table attribute like `VARCHAR(n)' is mapped onto String objects, while a foreign key constraint is translated into a object reference (aka pointer) representing the row of the key's target table. 3. Examples =========== The most basic example assumes a table representing a person. It has two data columns, `firstName' and `lastName', both of type string. Additionally, it has an integer primary key column (implicitly named `id') that is fed from a database sequence `Seq_Person'. Class definition in the meta-data file: <class name="Person"> <idField sequenceName="Seq_Person" /> <field name="firstName" schemaName="first_name"> <string size="128" /> </field> <field name="lastName" schemaName="last_name"> <string size="128" /> </field> </class> The table definition for our RDBMS system (Postgres in this case) looks like this: CREATE SEQUENCE Seq_Person; CREATE TABLE Person ( id INTEGER CONSTRAINT nn_Person_id NOT NULL, CONSTRAINT pk_Person PRIMARY KEY(id), first_name VARCHAR(64) CONSTRAINT nn_Person_first_name NOT NULL, last_name VARCHAR(64) CONSTRAINT nn_Person_last_name NOT NULL ); Now to application's view on this table and its rows. First, create our object container `cnt' on top of the database connection `conn' and start a transaction: cnt = new PostgreSQLContainer(conn); cnt.beginTransaction(); Add an object to the database table `Person': person = new Person("Michael", "van Acken"); cnt.makePersistent(person); person.store(); Retrieve all `Person' objects from the corresponding table: q = cnt.newQuery(); q.addTableExpr(Person.cdeclPerson, false); res = q.execute(); At this place, `res[0,0]' holds the first object, `res[1,0]' the second, and so on. To retrieve a subset of all persons matching a particular pattern, one needs to add a filter predicate to the query: q = cnt.newQuery(); q.addTableExpr(Person.cdeclPerson, false); q.addConj(Predicate.like(new Member(0, Person.attrFirstName), new Literal("Mich%"))); res = q.execute(); This returns all `Person' objects whose first name begins with `Mich'. Removing a table row: person.delete(); person.store(); And finally, closing the transaction: cnt.commitTransaction(); 4. Package Overview =================== de.mguennewig.pobjects The base library for PObjects. de.mguennewig.pobjects.metadata The metadata related part of the PObjects library. de.mguennewig.pobjects.demo A very simple investment related demo that uses the PObjects library. See InvestmentDemo.xml for the metadata declaration. de.mguennewig.pobjects.event A simple event interface that can be used to listen to database changes, for example within a Swing application. de.mguennewig.pobjects.filesystem A simple container implementation that maps the file system tree onto a Record. de.mguennewig.pobjects.jdbc Contains PObject container implementations which use JDBC. de.mguennewig.pobjects.memdb A simple container implementation that just keeps all objects in memory instead of a persistent database. de.mguennewig.pobjects.optional Contains classes and packages that provide optional features. de.mguennewig.pobjtool The code generator for the "glue" code. de.mguennewig.pobjtool.ddl This contains database specific "metadata" classes for the SQL code generator. de.mguennewig.pobjform A PObjects based framework for Swing dialogs and HTML forms. de.mguennewig.pobjform.html The HTML form implementation of the PObjForm framework. de.mguennewig.pobjform.swing The Swing dialog implementation of the PObjForm framework. de.mguennewig.pobjimport A toolkit to import and export PObject databases from/into XML files. de.mguennewig.pobjsearch An generic search framework based on PObjForm. 5. Compiling from Source ======================== The project is compiled using `Apache Ant' [1] by just starting in the source root (where this file residences): ant or ant dist 6. Thread-safety and concurrency ================================ The library is not thread-safe. 7. References ============= 1. Apache Ant http://ant.apache.org/ 2. Checkstyle http://checkstyle.sourceforge.net/ 3. GNU Getopt for Java http://www.urbanophile.com/arenn/hacking/download.html 4. MySQL http://www.mysql.com/ 5. Optimizing Oberon-2 Compiler http://ooc.sourceforge.net/ 6. Oracle Database http://www.oracle.com/database/ 7. PostgreSQL http://www.postgresql.org/ 8. Servlet Java Classes http://java.sun.com/products/servlet/