|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 6
The three discoveries listed above lead us to define a persistence layer, a framework that provides a high-level Java API for objects and relationships to outlive the runtime environment's (JVM) lifespan. Such a framework must feature the following qualities:
I detail most of the above qualities in the following sections.
Simplicity rates high on my list of required traits for any software framework or library (see this article's opening quote). Developing distributed applications is already hard enough, and many software projects fail because of poor complexity (and, by extension, risk) management. Simple is not synonymous with simplistic; the software should have all the needed features that allow a developer to do his/her job.
Every persistent storage system introduces a certain amount of intrusion into the application code. The ideal persistence layer should minimize intrusion to achieve better modularity and, thus, plug-and-play functionality.
For the purpose of this article, I define intrusion as:
Persistable or the like -- or by postprocessing the generated code
Intrusion also applies to object-oriented database systems and, although usually less of an issue there compared to relational data stores, it can vary significantly among ODBMS (object-oriented database management system) vendors.
The persistent layer transparency concept is pretty simple: the application uses the same API regardless of the data-store type (data storage-type transparency), or the data-store vendor (data storage-vendor transparency). Transparency greatly simplifies applications and improves their maintainability by hiding data-store implementation details to the maximum extent possible. In particular, for the prevalent relational data stores, unlike JDBC, you don't need to hardcode SQL statements or column names, or remember the column order returned by a query. In fact, you don't need to know SQL or relational algebra, because they're too implementation specific. Transparency is perhaps the persistence layer's most important trait.
The persistence layer API boils down to a relatively small set of operations:
An example of a PersistenceLayer API:
public void persist(Object obj); // Save obj to the data store.
public Object load(Class c, Object pK); // Read obj with a given primary key.
public void update(Object obj); // Update the modified object obj.
public void delete(Object obj); // Delete obj from the database.
public Collection find(Query q); // Find objects that satisfy conditions of our query.
A good persistence layer needs several elementary functions to start, commit, or roll back a transaction. Here is an example:
// Transaction (tx) demarcation.
public void startTx();
public void commitTx();
public void rollbackTx();
// Choose to make a persistent object transient after all.
public void makeTransient(Object o)
Note: Transaction demarcation APIs are primarily used in nonmanaged environments. In managed environments, the built-in transaction manager often assumes this functionality.
Managed environments, such as J2EE application servers, have grown popular with developers. Who wants to write middle tiers from scratch these days when we have excellent application servers available? A decent persistence layer should be able to work within any major application server's EJB (Enterprise JavaBean) container and synchronize with its services, such as JNDI (Java Naming and Directory Interface) and transaction management.
The API should be able to issue arbitrary queries for data searches. It should include a flexible and powerful, but easy-to-use, language -- the API should use Java objects, not SQL tables or other data-store representations as formal query parameters.
Cache management can do wonders for application performance. A sound persistence layer should provide full data caching as well as appropriate APIs to set the desired behavior, such as locking levels, eviction policies, lazy loading, and distributed caching support.
Providing automatic identity generation for data is one of the most common persistence services. Every decent persistence layer should provide identity generation, with support for all major primary key-generation algorithms. Primary key generation is a well-researched issue and numerous primary key algorithms exist.
With relational databases, a data mapping issue arises: the need to translate objects into tables, and to translate relationships, such as dependencies and references, into additional columns or tables. This is a nontrivial problem in itself, especially with complex object models. The topic of object-relational model impedance mismatch reaches beyond this article's scope, but is well publicized. See Resources for more information.
The following list of extras related to mapping and/or relational data stores are not required in the persistence layer, but they make a developer's life much easier:
Bar referencing another object of type Bar, for example
Example
The following code snippet shows how to use the persistence layer API. Suppose we have the following domain model: A company
has one or more locations, and each location has one or more users. The following could be an example application's code: