DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. db4o: Simple POJO Persistence

db4o: Simple POJO Persistence

German Viscuso user avatar by
German Viscuso
·
Mar. 05, 10 · Interview
Like (0)
Save
Tweet
Share
24.45K Views

Join the DZone community and get the full member experience.

Join For Free

Ranging from mobile to web applications, and from plain old Java to Scala or Groovy dialects, a modern Java developer always needs an ace in the hole when it comes to dealing with data persistence. Ideally, you're looking for a solution that gives you enough power to handle your domain complexity while being simple enough to boost your productivity by avoiding painful configurations or steep learning curves.

On one side of this scenario you have relational databases with ORM tools which force you to go through several steps like the creation of an object mapping file, a database configuration file, a helper class to initialize a session factory and class association mappings (i.e. precious time spent on downloading, installing, configuring, and then writing a lot of XML). On the other side there's just plain old serialization (mostly ending up in XML files). But with Java serialization you quickly run into a critical caveat: objects must be marked by implementing the java.io.Serializable interface. However, just adding "implements Serializable" to a class definition doesn't automatically make it serializable. The instance variables of the class must also be serializable (bummer). If this is not the case and you try to serialize the class an exception would be thrown. Unfortunately serialization in Java is not a straight forward solution for object persistence. There are many additional pitfalls associated with the serialization process such as object versioning problems, breaking of object identity and schema evolution issues (among others).

Right in the middle, being powerful enough to handle complex domains but so simple that you can actually store objects with one line of code, there's db4o, a persistence engine that was conceived to make the Java developer's life easier. Why define a separate schema for your data if your classes define it perfectly? With db4o your object model is your database schema. When you define the structure of your classes you're not only making a statement about how your objects should be run but also about how they should be made persistent! And why settle with object serialization? Serialization is a popular alternative to avoid complex persistence settings but can handle a very limited number of simple scenarios. 

So, what if you could have the power of an ACID database with the simplicity of use of object serialization?

In this article I will show you how to leverage the different features of db4o in different Java based scenarios ranging from mobile to enterprise applications with a focus on real samples taken from real db4o based projects.

Using db4o in the Griffon framework

Griffon is a Grails like application framework for developing desktop applications in Groovy. A db4o plugin (griffon-db4o v0.1) enables lightweight access to database functionality using db4o.
Upon installation the plugin generates the files Db4oConfig.groovy (that contains the datasource definition) and BootstrapDb4o.groovy (which defines init/destroy hooks for data to be manipulated during app startup/shutdown). Finally a new dynamic method named withDb4o is injected into all controllers, giving you access to a com.db4o.ObjectContainer instance, with which you'll be able to make calls to the database.

Consider the class:
class Person { 
        int id
        String name
        String lastname
}
Bootstrapping data could be filled in with this simple code:
class BootstrapDb4o {
def init = { db4o ->
db4o.store(new Person(id: 1, name: "Danno", lastname: "Ferrin"))
db4o.store(new Person(id: 2, name: "Andres", lastname: "Almiray"))
db4o.store(new Person(id: 3, name: "James", lastname: "Williams"))
db4o.store(new Person(id: 4, name: "Guillaume", lastname: "Laforge"))
db4o.store(new Person(id: 5, name: "Jim", lastname: "Shingler"))
db4o.store(new Person(id: 6, name: "Josh", lastname: "Reed"))
db4o.store(new Person(id: 7, name: "Hamlet", lastname: "D'Arcy"))
}

def destroy = { db4o ->
}
}

Implementing a controller that will react to an application event, load the data into a temporal List and update model.personsList inside the EDT is as simple as this:

class SampleController {
def model

def onStartupEnd = { app ->
withDb4o { db4o ->
def tmpList = db4o.query(Person)
edt { model.personsList.addAll(tmpList) }
}
}
}

db4o and Scala

Not surprisingly db4o is a perfect fit for Scala. It's so straight forward that you can implement master/slave replication of persistent objects in under 100 lines of Scala code. The code to bring the slave up-to-date comes down to this:

private def bringSlaveUpToDate() {
   println("starting replication...")
   
   val replication = Replication.begin(master, slave, replicationListener)
   
   val changes = replication.providerA().objectsChangedSinceLastReplication().iterator()
   if (!changes.hasNext) {
     println("Nothing to replicate")
     return
   }
   
   while (changes.hasNext()) {
     val changed = changes.next();
     replication.replicate(changed)
   }
  
   removeCommitListener
   try {
     replication.commit()
   } finally {
     listenToMasterCommits
   }
   
   println("replication finished")
 }
And usage of this Scala replication agent is amazingly simple:
package instant;

import com.db4o._
import com.db4o.config._
import scala.actors.Actor._
import scala.concurrent.SyncVar

case class Item(name: String)

object Main {
 
  def main(args : Array[String]) : Unit = {
    
    deleteFile("master.db4o"); deleteFile("slave.db4o")
    
    val slave = Db4o.openFile(configuration, "slave.db4o")
    val masterServer = Db4o.openServer(configuration, "master.db4o", -1)
    val masterClient = masterServer.openClient(configuration)
    for (i <- 1 to 10) {
      masterClient.store(Item("Item " + i))
    }
    masterClient.commit()
    
    try {
      
      val agent = new ReplicationAgent(masterServer.ext.objectContainer, slave)
      agent.start()
      
      val finished = new SyncVar[boolean]
      
      val masterUser = actor {
        for (i <- 10 to 150) {
          val item = Item("Item " + i)
          masterClient.store(item)
          
          println("Committing " + item)
          masterClient.commit()
        }
        finished.set(true)
      }
      
      finished.get
      
      println("stopping agent")
      agent.stop
      
    } finally {
      withErrorHandling { masterServer.close }
      withErrorHandling { slave.close }
    }
  }
 
  def withErrorHandling(block : => Unit) {
    try {
      block
    } catch {
      case e => println(e.toString)
    }
  }
 
  def configuration = {
    val c = Db4o.newConfiguration()
    c.generateUUIDs(ConfigScope.GLOBALLY)
    c.generateVersionNumbers(ConfigScope.GLOBALLY)
    c
  }
 
  def deleteFile(fname: String) = new java.io.File(fname).delete()
}
Servlet with db4o

db4o can also be used in a classic Java web environment because the database works in thread-safe mode. Servlets can be accessed by multiple threads since transactions in db4o work in a thread-safe manner.

First you start by copying the db4o library to the WEB-INF/lib directory. This allows your application to access the class responsible for the creation of server and client. Then you need to configure access to the database file and provide the means to create an ObjectServer for each application. One great way to do this consists in creating the ObjectServer in the application context startup and close the connection when the context has ended. We can do this by elaborating a Listener class:
package myproject.dao;   
     
    import javax.servlet.ServletContext;   
    import javax.servlet.ServletContextEvent;   
    import javax.servlet.ServletContextListener;   
    import com.db4o.Db4o;   
    import com.db4o.ObjectServer;   
     
   public class Db4oServletContextListener implements ServletContextListener {   
      public static final String KEY_DB4O_FILE_NAME = "db4oFileName";   
    
      public static final String KEY_DB4O_SERVER = "db4oServer";   
    
      private ObjectServer server = null;   
    
      public void contextInitialized(ServletContextEvent event) {   
         close();   
         ServletContext context = event.getServletContext();   
         String filePath = context.getRealPath("WEB-INF/db/" + context.getInitParameter(KEY_DB4O_FILE_NAME));   
         server = Db4o.openServer(filePath, 0);   
         context.setAttribute(KEY_DB4O_SERVER, server);   
         context.log("db4o startup on " + filePath);   
      }   
    
      public void contextDestroyed(ServletContextEvent event) {   
         ServletContext context = event.getServletContext();   
         context.removeAttribute(KEY_DB4O_SERVER);   
         close();   
         context.log("db4o shutdown");   
      }   
    
      private void close() {   
         if (server != null) {   
            server.close();   
         }   
         server = null;   
      }  
   }  
It is very easy to understand the code above. When the context is started a db4oServer is instantiated. And when stopped the connection with the database is ended. Don't forget to configure the application context Listener in the web.xml file present in WEB-INF directory:
<listener>  
      <listener-class>meuprojeto.dao.Db4oServletContextListener</listener-class>  
</listener> 
Note that you have to provide the full name of the Listener class (including the package). Finally it's time to configure the db4o file path used by the Listener:
<context-param>  
      <param-name>db4oFileName</param-name>  
      <param-value>web.yap</param-value>  
</context-param>  
Android persistence: db4o

When you consider SQLite, the defacto database for Android, and compare it with db4o you'll realize that there are many benefits when using object persistence in your mobile application: easier code maintenance due to simplicity, and the ability to create a variety of new, innovative applications based on more complex data models. Unlike in rigid, predefined SQL tables, db4o allows the storage of dynamic, free-form data, which can be changed or amended any time (which is specially useful when sending updates to your users). In addition, db4o allows for efficient data replication with its db4o Replication System (dRS), another missing element in Android's software stack.

Let's consider an insert and update operation with SQLite on Android:
//SQLite Insert
public void addPassword(PassEntry entry) {
    ContentValues initialValues = new ContentValues();
    initialValues.put("password", entry.password);
    initialValues.put("description", entry.description);
    initialValues.put("username", entry.username);
    initialValues.put("website", entry.website);
    initialValues.put("note", entry.note);
    db.insert(DATABASE_TABLE, null, initialValues);
}

//SQLite Update
public void updatePassword(long Id, PassEntry entry) {
    ContentValues args = new ContentValues();
    args.put("password", entry.password);
    args.put("description", entry.description);
    args.put("username", entry.username);
    args.put("website", entry.website);
    args.put("note", entry.note);
    db.update(DATABASE_TABLE, args,"id=" + Id, null);
}
If you choose db4o you can get rid of the code above and replace it with this method:
//db4o Upsert
public void savePassword(PassEntry entry) {

    if(entry.id == 0)
        entry.id = getNextId();
    db().store(entry);
    db().commit();
}
which serves as both insert and update (store acts as update if the object already exists and that's determined automatically by db4o).

db4o Spring integration

Want to use db4o for lightweight object persistence in your spring based application? Try a db4o extension that has been around for some time, the 'spring-db4o' module from Spring Modules. It is quite useful for anyone integrating db4o into an enterprise application to benefit from declarative transaction management, exception translation, and ease of configuration.

Configuration is pretty straight forward. In order to create a memory based db4o ObjectContainer, the following configuration can be used:

<bean id="memoryContainer" class="org.db4ospring.ObjectContainerFactoryBean">
   <property name="memoryFile">
     <bean class="com.db4o.ext.MemoryFile"/>
   </property>
</bean>

For an ObjectContainer connected to a (remote) server:

<bean id="remoteServerContainer" class="org.db4ospring.ObjectContainerFactoryBean">
   <property name="hostName" value="localhost"/>
   <property name="port" value="123"/>
   <property name="user" value="foo"/>
   <property name="password" value="bar"/>
</bean>

While creating a database file based, local ObjectContainer can be achieved using a bean definition such as:

<bean id="fileContainer" class="org.db4ospring.ObjectContainerFactoryBean">
   <property name="databaseFile" value="classpath:db4o-file.db"/>
</bean>
The core classes of db4o module that are used in practice, are Db4oTemplate and Db4oCallback . The template translates db4o exceptions into Spring Data Access exception hierarchy (making it easy to integrate db4o with other persistence frameworks supported by Spring) and maps most of db4o's ObjectContainer and ExtObjectContainer interface methods, allowing one-liners:
db4oTemplate.activate(personObject, 4); // or
db4oTemplate.releaseSemaphore("myLock");
The db4o module also provides integration with Spring's excellent transaction support through Db4oTransactionManager class. Since db4o statements are always executed inside a transaction, Spring transaction demarcation can be used for committing or rolling back the running transaction at certain points during the execution flow.

The bottom line

Many Java developers are already enjoying the simplicity of object persistence with db4o. Before choosing a relational datastore with ORM mapping or XML serialization for your next project ask yourself whether you can make things simpler and faster by just using db4o. Take db4o for a ride, you won't regret it!
Db4o Persistence (computer science) mobile app Object (computer science) Relational database Spring Framework Data (computing) Java (programming language) Serialization

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • OpenVPN With Radius and Multi-Factor Authentication
  • Unlocking the Power of Elasticsearch: A Comprehensive Guide to Complex Search Use Cases
  • Building a RESTful API With AWS Lambda and Express
  • Microservices Testing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: