First Replication
Once you have a database created and filled with patient information, suppose you want to replicate that information into another database. I refer to the original database as PATIENTA and the database being replicated into as PATIENTB.
In db4o, replication is a three-step process:
- First, you must instantiate a ReplicationProcess object. This object must implement a ReplicationConflictHandler(), which is the deus ex machina callback method mentioned earlier. This method is called whenever the version information in both objects has changed since the last synchronization (per db4o's replication plan).
- Next, you define a query that guides the replication process. In most cases, this query is crafted to return all the objects in the database (thus causing all objects to be replicated). You can modify the query to restrict it to a subset of the database's objects, and thereby fine tune which objects participate in the replication.
- Finally, you execute the query. Each object returned by the query is handed over to the ReplicationProcess object's replicate() method, which does all the low-level dirty work. It all looks like Listing Three.
// Set things up for replication Db4o.configure().generateUUIDs(Integer.MAX_VALUE); Db4o.configure().generateVersionNumbers(Integer.MAX_VALUE); // Open both databases ObjectContainer patientADB = Db4o.openFile("PATIENTA.YAP"); ObjectContainer patientBDB = Db4o.openFile("PATIENTB.YAP"); // Create a ReplicationProcess object patientADB.ext().replicationBegin( patientBDB, new ReplicationConflictHandler() { public Object resolveConflict( ReplicationProcess replicationProcess, Object a, Object b) { return a; } }); // PATIENTB replication.setDirection(patientADB,patientBDB); // Do the replication Query q = patientADB.query(); ObjectSet replicationSet = q.execute(); while (replicationSet.hasNext()) { replication.replicate(replicationSet.next()); } replication.commit(); // Close both databases patientADB.close();
After opening the databases, you create a ReplicationProcess object via a call to the replicationBegin() method of the ObjectContainer's extended interface (available through the ext() method). The replicationBegin() method is called on the primary database's ObjectContainer, and takes as its first argument the secondary database's ObjectContainer. Its other argumentwhich we have supplied as an anonymous objectis the ReplicationConflictHandler object. As you can see, our conflict handler is rather simple. If given a choice between Object a (from PATIENTA) or Object b (from PATIENTB), we choose Object a every time.
Next, we call setDirection() on the ReplicationProcess object, specifying the direction of the replication as going from PATIENTA to PATIENTB.
The query object I create in the next section of code is simply an empty query, which returns all objects in the database. The execution of the query returns an ObjectSet, through which we iterate to process all the objects that are to be replicated by passing those objects into the replicate() method. When it's all done, we commit() the query and close the databases; see Listing Four.
// Set things up for replication Db4o.configure().generateUUIDs(Integer.MAX_VALUE); Db4o.configure().generateVersionNumbers(Integer.MAX_VALUE); // Open both databases ObjectContainer patientADB = Db4o.openFile("PATIENTA.YAP"); ObjectContainer patientBDB = Db4o.openFile("PATIENTB.YAP"); // Create a ReplicationProcess object ReplicationProcess replication = patientBDB.ext().replicationBegin( patientADB, public Object resolveConflict( ReplicationProcess replicationProcess, Object b, Object a) { return b; } }); // Set the direction from PATIENTB to replication.setDirection(patientBDB,patientADB); // Do the replication replication.whereModified(q); ObjectSet replicationSet = q.execute(); while (replicationSet.hasNext()) { replication.replicate(replicationSet.next()); } replication.commit(); // Close both databases patientADB.close(); patientBDB.close();
Pretty simple. And, happily, synchronizing any changes in PATIENTB back to PATIENTA is equally simple. With one small addition, this code is more or less a mirror image of the aforementioned code. (It's a mirror image because we're replicating in the opposite direction.)
As you can see, this code is virtually identical to its preceding cousin. Most differences stem from the fact that the direction is now from PATIENTB to PATIENTA. Hence, the ReplicationProcess object and the conflict resolution handler are modified accordingly. In addition, the setDirection() method identifies PATIENTB as the originating database.
The other addition is a constraint that I attached to the query that drives the replication. Specifically, I called the ReplicationProcess's whereModified() method, with the query as its sole argument. This constrains the query so that it returns only those objects that have changed since the last replication. This significantly speeds the process because objects that have not changed need not be handed to the replication code at all. (New objects are, of course, considered by the replication process to be modified.)
Replication Complete
The complete source code for the patient database is available electronically; see "Resource Center," page 5. The code consists of a number of small, easily extended applications. One creates the primary patient database; another replicates its contents to a secondary database. Another application modifies the secondary database, and I've provided code for replicating back to the master, as well as a simple application that displays the database contents.
You can explore a database even deeper with db4o's ObjectManager. This is a standalone database browser application that lets you open a database and navigate through all its contained objects, object references, data member fields, and so on. You can even modify individual data members. In short, with the help of ObjectManager, you can experiment with more complex replication code, and examine the resulting database to see the effects.