Java is an object-oriented language, and developers inherently work with objects, which at any time have a state represented by member variables . From the Java developer's perspective, persistence focuses on the fact that the state in these objects should be available after the Java virtual machine has exited. For example, an application passing and using an instance of an Order class should be capable of recreating the Order instance after a machine reboot, or a year later when the account is audited . The traditional programming approach has been to acquire the relevant state information and store it externally in some form for example, using a relational database. When required, a new object instance is created and populated with this previously stored state. It is the developer's responsibility to map the information in the underlying datastore to its programmatic representation, as illustrated in Figure 1-2: Figure 1-2. Traditional persistence deals with information.
For example, to save an Order object from Figure 1-1(b), developers write code to insert order attributes (date, amount, etc.) and dependent attributes like those in the LineItem in some datastore. When needed, the datastore is queried for some field (like an orderid) that uniquely identifies the collective information set about that order and a new Order instance is created and populated with that information. In general Java applications have followed variations of the following development steps:
As shown in Figure 1-3, developers now have to define, build, and use two models that programmatically reflect the same business domain for example, business objects in the application code and their relational representation in the database. They also have to ensure that the view and behavior of these two models remains consistent because the slightest variation in either will result in an impedance mismatch between the models and the real-world business. Figure 1-3. Traditional persistence leads to two models.
The idea of orthogonal persistence is one in which the application design is decoupled and independent of the entire underlying persistence infrastructure and has been around for a while. [1] An application is said to exhibit orthogonal persistence when the following three principles [2] that have been established after many years of research into persistence are met:
The first attempts at building orthogonal persistence using these principles in Java were initiated by Sun Laboratories in July 1995 as a research project called "Forest" and resulted in a prototype design called PJama. [3] However, the design was based around a modified virtual machine, and a second research project called JSPIN [4] tried another approach with the use of a preprocessor based on earlier research on ADA83. Although JDO is unrelated to these efforts, the principles outlined in all these works JDO share a conceptual similarity. In practical terms, JDO fulfills the needs of the community with a much simpler architecture.
An architecture utilizing any form of orthogonal persistence is quite different from the traditional persistence approach that we looked at earlier, in that this model deals with objects directly rather than the information contained therein. Such a persistence service abstracts away all the semantics relating to persistence from the application. Rather than working with information and mapping that information back and forth between objects, developers work with persistent objects directly in their code just like any other application objects. The difference is that some of these objects now represent persistent information in a datastore, as illustrated in Figure 1-4. The details about how they are persisted and recreated are transparent to the developer; this is often referred to as transparent persistence. Figure 1-4. Transparent persistence deals directly with objects.
For example, to save an Order object from Figure 1-1(b), developers would use a transparent persistence service and give it that particular Order instance. When needed by the application at a later time, developers would query the service for that unique Order, by using, for example, an attribute "give me the order object with the orderid of 2828." The previously stored object matching that criterion is returned by the service. JDO is the standard that provides transparent persistence services to Java applications. With JDO, architects and developers can concentrate on the application at hand, rather than the information contained therein. In comparison to the previously listed steps, development with JDO would involve the following steps:
As shown in Figure 1-5, design with transparent persistence is much more simplified. Developers need to write and maintain only a single model or programmatic representation, e.g., business objects in the application code, when needed entities from that model can be persisted using the transparent persistence service. Figure 1-5. The transparent persistence model.
Of course, JDO is not the single solution to all persistence- related issues, and some situations in which this might not be appropriate are discussed in Chapter 13. However, one thing is clear: JDO greatly simplifies (and possibly speeds up) the development approach.
1.3.1 Transient and persistent objectsJava applications instantiate objects using the new operator and the constructor. For example: Order myorder= new Order(2828); myorder.addItem( new LineItem ("Core JDO",".99")); When instantiated , the virtual machine allocates space on the heap in memory, creates the object, and returns a reference. These objects do not represent any persistent data, meaning that their state and the state of objects referenced by them is present only in the memory of the virtual machine in which they are created. Such objects are called transient because they live only as long as the virtual machine process is running, or unless they are garbage collected. A persistent object is no different from any other object, in that it is created using the same new operator and constructor. However, a persistent object is an instance of a class that has been marked as persistence capable and represents data in some persistent store. The lifespan of persistent objects is not tied to the life of the virtual machine in which they are created. A persistent object becomes the representation of persistent data as a result of an explicit invocation of JDO API. For example: PersistenceManager mgr=pmf.getPersistenceManager();// JDO API mgr.currentTransaction().begin()/ JDO API Order myorder= new Order(2828); // Transient object mgr. makePersistent (myorder); // myorder marked as persistent mgr.currentTransaction().commit() // JDO API instance myorder //is written to datastore A transient object can also become persistent if it is referenced as a field in a persistent object. This is known as persistence by reachability or transitive persistence (in ODMG terms), and it ensures that when an object is stored in a datastore, every item or the graph of items that the object references and collectively represent that object's state are also preserved. This is necessary to recreate the object instance correctly when required. Persistence by reachability is the aspect that results in the appearance of transparent persistence to the developer. The code extract below explains this further: PersistenceManager mgr=pmf.getPersistenceManager();// JDO API mgr.currentTransaction().begin();// JDO API Order myorder= new Order(2828); // Transient object mgr. makePersistent (myorder); // myorder marked as persistent // other application code here myorder.addItem(new LineItem ("Core JDO",".99")); mgr.currentTransaction().commit();// JDO API myorder //including the LineItem is //also written to datastore In the above code segment, unlike the Order object, the LineItem object was never explicitly persisted. However, because it is referenced by an object that is persisted, it too will be saved, as depicted here in Figure 1-6: Figure 1-6. Persistence by reachability.
A persistent object can be changed back to a transient object by an explicit call to the JDO API. When a persistent object is marked as transient, any change made to it is not reflected in the datastore. For example: PersistenceManager mgr=pmf.getPersistenceManager();// JDO API mgr.currentTransaction().begin(); // JDO API // other application code here mgr. makeTransient (myorder) // JDO API // other application code here A persistent object is also automatically changed to a transient object after it has been persisted. For example, look again at the code shown earlier: PersistenceManager mgr=pmf.getPersistenceManager();// JDO API mgr.currentTransaction().begin()/ JDO API Order myorder= new Order(2828); // Transient object mgr. makePersistent (myorder); // myorder marked as persistent mgr.currentTransaction().commit() // JDO API instance myorder //is written to datastore myorder.doSomething(); // Transient object Both transient and persistent instances go through different state changes that we discuss in Chapter 4. Another strategy employed in object persistence is termed persistence by inheritance , in which object instances of a subclass of a persistence-capable class can be persisted. JDO is very complete in its support for inheritance. A class can be persistence capable even if its parent is not, a parent can be persistence capable and sub classes may not be, and even selective individual classes may be persistence capable in a large inheritance tree. It is up to the requirements specified by the developer. The key architectural points surrounding JDO persistence are as follows :
|