Feature
Evaluating Options for Persisting Java Objects
Hibernate, DB4O, and Caché Database with Jalapeño
Jun. 2, 2007 05:15 PM
DB4O
DB4O provides three different ways to retrieve your
objects: Query By Example, Native Queries, and SODA Queries. Each has
its pros and cons.
The simplest and most limited is QBE. With this method, you create a
prototype of the object you're looking for using one of the object's
constructors, and DB4O returns the matching records.
// QBE Example - Retrieve Person By Name
Person proto=new Person("Ben","Franklin",0);
ObjectSet result=db.get(proto);
listResult(result);
Native Queries are the preferred query method and are typesafe, compile-time checked, and refactorable.
Caché
Cache provides an object oriented query mechanism
that uses SQL forselection and which returns an iterator you can use to
traverse the returned objects. You also have the option of using JDBC
and SQL to perform complex queries over multiple related objects (SQL
JOINS) or VIEWS. The results of these complex queries can also be
accessed as objects, as long as the Object ID is returned as part of
the query.
import com.intersys.pojo.ApplicationContext;
import com.intersys.pojo.ObjectManager;
try {
ObjectManager objectManager;
String url="jdbc:Caché ://localhost:1972/" + namespace;
objectManager = ApplicationContext.createObjectManager (url, username, password);
String sql = "Name %startsWith ?"; // Search for people by name
if ("null".equalsIgnoreCase (query)){query = null;
}
String[] qargs = {query};
Iterator people = objectManager.openByQuery (Person.class, sql, qargs);
while (people.hasNext()){IPerson person = (Person) people.next();
System.out.print ("Name: " + person.getName() );
}
objectManager.close ();
} catch (Exception ex) {
System.out.println( "Caught exception: " + ex.getClass().getName() + ": " + ex.getMessage() );
ex.printStackTrace();
}
Controlling Object Depth
One of the challenges of
storing objects is to control how many objects are stored and retrieved
at one time. For example, consider a company that contains 100
departments and each department contains 25 to 50 employees. Using Java
Serialization, serializing the Company object would also persist all
the Department and Employee objects - and instantiating the Company
would also instantiate the related Department and Employee objects. If
all you need to persist is the Company Object, you've done a lot of
unnecessary work! There's considerable impact on performance and memory
requirements for every object instantiated. Furthermore, if you send
this object over the network, you want to be as efficient as possible -
and therefore only send the Company Object.
What's needed is a mechanism for controlling how deeply you traverse the object tree when instantiating objects.
Hibernate
Hibernate handles this well because it's based
on a relational database structure. It is easy to control the depth of
the data (or the depth of the objects) returned by specifying LAZY or
EAGER loading and if you switch to JDBC, you have total control using
SQL JOIN statements.
When you specify LAZY loading, the POJO is actually replaced with a
proxy object that will load properties and collections via the
Hibernate session as needed.
DB4O
DB4O provides excellent control over object depth.
You can easily specify exactly how many levels you want to retrieve or
update. You can also turn on cascading updates/deletion - which
traverses the entire object graph:
Db4o.configure().objectClass(Car.class).cascadeOnUpdate(true);
Or you can specify the activation depth when selecting/updating/deleting:
SensorReadout readout=car.getHistory();
while(readout!=null) {
db.activate(readout,2); // Activate the next 2 object levels!!!
System.out.println(readout);
readout=readout.getNext();
}
Thus you can specify: "Get the next three levels only" if desired or "Get everything!"
CAUTION:
DB4O doesn't enforce referential integrity, so be very careful when
deleting with cascade delete enabled. You can delete objects that are
still pointed to by other objects in the database.
Caché
Caché provides good control over object depth as
well. You can specify FetchType = Eager or Lazy like Hibernate. Calling
the "detach(Object)" method ensures that all data in the given object
(including all objects reachable from it by following references) can
be accessed without a connection to the database.
Once again, if you switch to JDBC/SQL, you have total control over
object depth via SQL JOIN statements, but can still open the objects
referenced in the resultset.
About Richard ConwayRichard Conway is a software developer and technology consultant with more than 15 years of technology, project management, and information services experience. He has extensive experience developing Java/Struts-based web applications. He started focusing more on Swing based developments at the beginning of 2005 and has just finished a Swing-based client/server asset management project. He lives in Miami with his wife Patricia, is currently working on an EMR application, and plays sand volleyball in his spare time.