[ODB1] Chapter 2 - A Quick Tour
This chapter introduces basic ObjectDB and JDO concepts, using two sample programs. We start with the HelloWorld sample program, which is not JDO portable because it uses some ObjectDB extensions, but it is a good sample to start with because of its simplicity. We then proceed with the JDO Person sample program, which demonstrates the process of building a minimal JDO portable application, step by step. Both sample programs are contained in ObjectDB's samples directory.
This chapter contains the following sections:
2.1 Hello World
The HelloWorld sample program manages a list of strings in the database. Each time the program is run another string is stored in the database and all the strings in the database are printed to standard output.
The output of the first run is expected to be:
Hello World 0
The output of the second run is expected to be:
Hello World 0 Hello World 1
After two runs, the database contains a list of two strings "Hello World 0" and "Hello World 1".
Program Source Code
The program consists of a single source file, HelloWorld.java, containing a single class:
1 // A simple program that manages a list of strings in a database. 2 3 import java.util.*; 4 import javax.jdo.*; 5 import com.objectdb.Utilities; 6 7 public class HelloWorld { 8 9 public static void main(String[] args) { 10 11 // Create or open a database and begin a transaction: 12 PersistenceManager pm = 13 Utilities.getPersistenceManager("hello.odb"); 14 pm.currentTransaction().begin(); 15 16 // Obtain a persistent list: 17 ArrayList list; 18 try { 19 // Retrieve the list from the database by its name: 20 list = (ArrayList)pm.getObjectById("Hello World", true); 21 } 22 catch (JDOException x) { 23 // If not found - construct and store a new list: 24 list = new ArrayList(); 25 Utilities.bind(pm, list, "Hello World"); 26 } 27 28 // Add a new string to the persistent list: 29 list.add("Hello World " + list.size()); 30 31 // Display the content of the persistent list: 32 Iterator itr = list.iterator(); 33 while (itr.hasNext()) 34 System.out.println(itr.next()); 35 36 // Close the transaction and the database: 37 pm.currentTransaction().commit(); 38 pm.close(); 39 } 40 }
How it works
Lines 3-5 | Three import statements are required: for Java collections (line 3), for JDO (line 4) and for ObjectDB extensions (line 5). |
Lines 11-14 | A |
Lines 16-26 | The data structure of this program is an Note: Memory objects that represent database objects (like |
Lines 28-29 | A new string ("Hello World 0", "Hello World 1", etc.) is added to the |
Lines 31-34 | The |
Lines 36-38 | On transaction commit (line 37), new persistent objects (like a new |
ObjectDB Extensions
The com.objectdb.Utilities
class implements ObjectDB extensions. Storing an ArrayList
directly in the database with a specified name (line 25), is supported by ObjectDB but not by JDO. In JDO, only instances of special user defined classes (which are called persistent classes) can be stored in the database directly, and other types (such as Java collections) can be stored only as fields of persistent classes. In addition, objects are stored without names in JDO. Another extension is used to obtain a PersistenceManager instance (line 13). Obtaining a PersistenceManager in a portable JDO way, which is slightly more complicated, is demonstrated in the next program sample.
2.2 Defining a Persistent Class
As noted above, in a portable JDO application, only instances of persistent classes are stored directly in the database. Predefined Java types like ArrayList
, String
and Date
can be stored in the database only as fields of persistent classes.
Persistent Classes
Persistent classes are user defined classes whose instances can be stored in the database using JDO. The JDO specification refers to persistent classes as persistence capable classes, but both terms are equivalent. Chapter 3 is dedicated to persistent classes.
To become persistent, a class has to:
- be declared in a JDO metadata file in XML format.
- include a no-arg constructor.
- implement the
javax.jdo.spi.PersistenceCapable
interface.
The PersistenceCapable
interface includes more than 20 abstract methods, so implementing it explicitly is far from trivial. Therefore, persistent classes are usually defined without implementing that interface explicitly, and a special tool, the JDO enhancer, adds the implementation automatically.
The Person Persistent Class
The following Person
class serves as a persistent class:
1 // The Person persistent class 2 3 public class Person { 4 5 // Persistent Fields: 6 private String firstName; 7 private String lastName; 8 private int age; 9 10 // Constructors: 11 public Person() {} 12 public Person(String firstName, String lastName, int age) { 13 this.firstName = firstName; 14 this.lastName = lastName; 15 this.age = age; 16 } 17 18 // String Representation: 19 public String toString() { 20 return firstName + " " + lastName + " (" + age + ")"; 21 } 22 }
The Person
class is an ordinary Java class with a no-arg constructor (line 11). If it is declared as persistent in a JDO metadata file, it can easily become persistent using the JDO enhancer.
JDO Metadata
Every persistent class must be declared as persistent in a special XML file, called the JDO metadata file. The Person
class must be declared in a metadata file named either package.jdo (generic name) or Person.jdo (name of the class). More details about metadata files, including their location and naming rules, are provided in chapter 4, which is devoted to JDO metadata.
The following package.jdo file is located in the same directory as person.class:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE jdo SYSTEM "http://java.sun.com/dtd/jdo_1_0.dtd"> 3 4 <jdo> 5 <package name=""> 6 <class name="Person" /> 7 </package> 8 </jdo>
Every JDO metadata file contains a single <jdo>
element (lines 4-8). Each package is represented by a <package>
sub element (e.g. lines 5-7) containing <class>
elements for all the persistent classes in the package (e.g. line 6). Both <package>
and <class>
elements must have a name
attribute. An empty name is specified for the <package>
element to represent the default package (in which the class is defined). More information can be provided in the metadata file (as shown in chapter 4), but this minimal form is sufficient here.
2.3 Storing and Retrieving Objects
The JDO Person sample manages a collection of Person
instances in the database.
Three arguments have to be specified in order to run the program Main class:
> java Main George Bush 57
Each time the program is run, another Person
instance is constructed (according to the specified first name, last name and age) and stored in the database.
Program Source Code
In addition to the two files from the previous section (Person.java and package.jdo), the program contains the following Main.java source:
1 // Main of the JDO Person sample. 2 3 import java.util.*; 4 import javax.jdo.*; 5 6 public class Main { 7 8 public static void main(String[] args) { 9 10 // Check the arguments: 11 if (args.length != 3) 12 { 13 System.out.println( 14 "Usage: java Main <first name> <last name> <age>"); 15 System.exit(1); 16 } 17 18 try { 19 // Obtain a database connection: 20 Properties properties = new Properties(); 21 properties.setProperty( 22 "javax.jdo.PersistenceManagerFactoryClass", 23 "com.objectdb.jdo.PMF"); 24 properties.setProperty( 25 "javax.jdo.option.ConnectionURL", "persons.odb"); 26 PersistenceManagerFactory pmf = 27 JDOHelper.getPersistenceManagerFactory(properties); 28 PersistenceManager pm = pmf.getPersistenceManager(); 29 30 try { 31 // Begin the transaction: 32 pm.currentTransaction().begin(); 33 34 // Create and store a new Person instance: 35 Person person = new Person( 36 args[0], args[1], Integer.parseInt(args[2])); 37 pm.makePersistent(person); 38 39 // Print all the Persons in the database: 40 Extent extent = pm.getExtent(Person.class, false); 41 Iterator itr = extent.iterator(); 42 while (itr.hasNext()) 43 System.out.println(itr.next()); 44 extent.closeAll(); 45 46 // Commit the transaction: 47 pm.currentTransaction().commit(); 48 } 49 finally { 50 // Close the database and active transaction: 51 if (pm.currentTransaction().isActive()) 52 pm.currentTransaction().rollback(); 53 if (!pm.isClosed()) 54 pm.close(); 55 } 56 } 57 58 // Handle both JDOException and NumberFormatException: 59 catch (RuntimeException x) { 60 System.err.println("Error: " + x.getMessage()); 61 } 62 } 63 }
How it works
Lines 3-4 | Two import statements are required: for Java collections (line 3), and for JDO (line 4). ObjectDB extensions are not used in this program. |
Lines 19-28 | A |
Lines 31-32 | A transaction on the database is begun. |
Lines 34-37 | A new |
Lines 39-44 | The |
Lines 46-47 | Updates are physically applied to the database when the transaction is committed (line 47). |
Lines 49-55 | The database is closed (lines 53-54) in a |
Lines 58-61 | When working with JDO, instances of |
An attempt to run the program now results in the following error:
Error: Class 'Person' is not PersistenceCapable
Something is still missing to make this program work - the JDO enhancement.
2.4 On The Fly JDO Enhancement
On the fly JDO enhancement is the easiest way to use the JDO enhancer. To use it, an additional main class, named eMain
(enhancer Main) is defined:
1 // An enhancer main class for the JDO Person Sample 2 3 public class eMain { 4 5 public static void main(String[] args) { 6 7 // Enhance the Person class if necessary: 8 com.objectdb.Enhancer.enhance("Person"); 9 10 // Now run the real main: 11 Main.main(args); 12 } 13 }
Class eMain
replaces the original Main
class as a program entry point at development time. It starts by enhancing necessary classes (line 8). JDO enhancement includes modifying class files in order to add implementation of the PersistenceCapable
interface, where necessary, at the byte code level. When this process is completed the real main
is called (line 11). Of course, this arrangement should only be used during development. Eventually, the application should be deployed with ready to use enhanced classes and without the eMain
class.
The enhancer should print the following message (unless Person.class is already enhanced):
[ObjectDB Enhancer] 1 new persistence capable class has been enhanced.
If you see the following message, something went wrong:
[ObjectDB Enhancer] 1 new persistence aware class has been enhanced.
The enhancer did not find the metadata file in which Person
is declared as persistent. Therefore, it was not able to enhance the Person class as a persistent class (a persistence aware class is not a persistence capable class). Additional information on JDO enhancement, persistence capable classes and persistence aware classes is provided in section 3.3.