Scala and db4o

Recently, I’ve been looking at db4o – an open source object database. I have plenty of experience with various object relational mapping frameworks, but wanted to try an alternative framework,  one which should result in clean code, with no need to have the performance hit, and configuration complexity often associated with ORM.

As others have noted, db4o seems like a good fit with Scala. Hopefully the combination of the two should result in a more maintainable codebase.

I will show a couple of examples of the db4o API here. First I need to create a simple class that I will persist, and later query. For this I have created a Person class, that has only one field, a name, well I did say it was a simple class.

class Person(val name: String)

Opening a connection to the database, and persisting an instance of our Person class can be achieved with a few lines of code, as shown below:


val db = Db4o openFile "test.yap"

val me = new Person(“Matthew”)

db set me

This will open, or create a new file based database called “test.yap” in the current directory and persist the created instance of Person. Persisting objects really is as simple as that, no schema or mappings to define. The set method can be used to store changes to existing objects, as well as storing new objects.

The next obvious step is retrieving any objects we have persisted. Db4o offers three ways to do this:

  1. Query by example – This is a simple form of querying , one that can be found in most ORM frameworks too, and provides a way to query the database based on a ‘template’ instance of a class. All persisted instances matching this template will be returned.
  2. Native queries – I would imagine that most queries would be written this way.  Native queries provide a powerful way to write queries against the database in code, providing a great typesafe way to write queries.
  3. SODA query API – SODA is db4o’s internal query system. You wouldn’t normally need to write a query using SODA unless there were performance issues when using one of the other two query types. All types of queries actually get translated into a SODA query by the framework.

I will show an example of a native query here, as I believe this will be the more common type of query used. Writing a query class involves implementing the match method on the Predicate interface. This method takes one parameter, an instance of the class being queried, and returns a boolean – true if this query should return the given instance.

Thanfully Scala allows us to make the writing of Predicates even easier through the use of an implicit conversion. We can convert a function that takes an instance of our person class and returns a boolean into a Predicate quite easily.  An example of a simple query, is shown below:


implicit def toPredicate[T](predicate: T => Boolean) =
new Predicate[T]() {def `match`(entry: T): Boolean = {predicate(entry)}}

val result = db query {person: Person => person.name.contains("t") }

The example above shows our implicit conversion that allows for easy creation of (typesafe) queries, as well as a simple query that will return every Person persisted that has a letter ‘t’ in their name.

The query method returns an ObjectSet, a db4o class that can be used to iterate over every matching instance. This ObjectSet won’t play nice with Scala’s ‘for’ loop. Thankfully conversion of the ObjectSet into an iterable object can be easily achieved as shown below:


class RichObjectSet[T](objectSet:ObjectSet[T]) extends Iterator[T] {
def hasNext:Boolean =  objectSet.hasNext()
def next:T = objectSet.next()
}

implicit def toRichObjectSet[T](objectSet: ObjectSet[T] ) =
new RichObjectSet[T](objectSet)

Executing our original query and displaying the results can now be performed in a couple of lines of code as shown below:


val result = db query {person: Person => person.name.contains("t") }

for(person <- result) println(person.name)

So, as you can see persisting objects as well as running queries can be achieved with only a few lines of code.

Seeing this code, you may question the efficiency of the query. Surely db4o can’t be passing every persisted object to our match method. This would be extremely inefficient. As I hinted at previously, db4o will attempt to convert our query into it’s own internal SODA representation before executing. It achieves this transformation by examining the bytecode of our query before executing. If this translation to SODA fails, only then will db4o resort to calling our match method with every instance of our class we have persisted.

Unfortunately for my experiments in using db4o with Scala, the query optimiser does not seem to cope with the bytecode generated by the Scala compiler well, resulting in most queries failing to get converted to SODA. I am currently investigating ways in which the query optimiser can be made to work with Scala, and will post my findings here.

I hope this has given an insight into how object persistence and retrieval can be achieved in a typesafe manner in a few lines of code, with no complex mapping required. I think the simplicity of the API that db4o provides is certainly a good match with Scala and if the query optimisation issue can be solved, then one that is worth looking at further.

This entry was posted in Scala and tagged , . Bookmark the permalink.

7 Responses to Scala and db4o

  1. Joe says:

    Hi dear
    consider neodatis instead of db4o , it is of superior performance and is LGPL license instead of GPL
    (i do not work for them but i use neodatis and it rocks)
    joe

  2. Sebastien says:

    Does this solve your type issue with native queries:

    http://matlik.net/blog/2007/11/28/scala-and-db4o-native-queries/

  3. Hi Matthew,

    what Java, Scala and Db4o Version are you using? I tried the same approach a a short time ago and it resulted in an: java.lang.IllegalArgumentException (http://developer.db4o.com/forums/thread/55155.aspx) , thanks
    Alex

  4. Matthew says:

    Hi Alex,
    I’m using the Java 1.6.0, Scala 2.7.4, and had to download a development release of db4o – version 7.9. I hope this helps,
    Matthew

  5. Hi Matthew, thanks a lot for the information. I will try it with the 7.9 development release in the following days.

    Alex

  6. devorb says:

    Hi Matthew,
    I’m trying to use your example in a trait (just began working in scala a week ago :) so this is my code:


    import com.db4o.ObjectSet;
    import com.db4o.query.Predicate;

    trait Db4oHelper {
    implicit def toPredicate[T](predicate: T => Boolean) = new Predicate[T]() {def `match`(entry: T): Boolean = {predicate(entry)}}
    implicit def toRichObjectSet[T](objectSet: ObjectSet[T] ) = new RichObjectSet[T](objectSet)

    class RichObjectSet[T](objectSet:ObjectSet[T]) extends Iterator[T] {
    def hasNext:Boolean = objectSet.hasNext()
    def next:T = objectSet.next()
    }
    }

    but when I want to use it, in a class I allways get an exception
    java.lang.IncompatibleClassChangeError: Db4oHelper and Db4oHelper$$anon$1 disagree on InnerClasses attribute

    The problem is with the toPredicate line. Any ideas?

  7. Sam Stainsby says:

    You might want to look at my Scala/DB4O/Wicket project:
    http://uniscala.net/granite/
    which is currently experimental, but looking at beta soon.
    Generally, my experience is that SODA queries are the best way to go with Scala and DB4O. Native queries do work, but optimisation of native queries on on-the-fly, or code injection, does not work. Thus TA/TP also hit problems.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>