Basic Concept
Three basic concepts summarize the operation of the persistent object abstraction layer:
- Every table corresponds to a class.
- Every table row corresponds to an instance of the table's class.
- 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.
Basic Assumptions
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.