En la línea de les metodologías ágiles y la innovación en modelos de bases de datos, la incorporación de una base de datos orientada a objetos en un nuevo sistema software puede accelerar de forma significativa el desarrollo de un proyecto, además de simplificar toda la lógica a implementar.

OODBs: un cambio de paradigma

Las bases de datos orientadas a objetos (OODB, en adelante) no son un paradigma nuevo en el mundo de la persistencia de datos. Los primeros sistemas de gestión de estas BDs se remontan a los años 80, con la aparición de Term Object Database y Versant Object Database, pero todos persiguen un mismo objetivo: llevar la orientación a objetos hasta la misma capa de datos de las aplicaciones, lo que se traduce en no tener que convertir los objetos que tienen que persistir en un esquema relacional.

Con una OOBD, los desarrolladores podemos simplificar mucho el diseño de los métodos de acceso, modificado y borrado de objetos en la base de datos. No hace falta que nos preocupemos de las referencias entre unos y otros objetos, ya no hacen falta atributos de identificación ni claves primarias ni foráneas. Con una OODB no tenemos que pensar cómo mapear los atributos de los objetos, porque guardamos los propios objetos!

db4o

Entre otros sistemas de gestión para bases de datos de objetos, nos encontramos con db4o, el cual mantiene y desarrolla Versant después de comprarlo en 2008. Se trata de una OODB ligera que, además, se puede utilizar de manera integrada (embedded) en el resto de la aplicación que se esté utilizando; no es necesario, por tanto, un servidor externo donde la aplicación haga una conexión.

Antes de usar db4o, hará falta que aprendamos un poco sobre su funcionamiento (está bastante bien documentado en http://community.versant.com/documentation/reference/db4o-8.1/java/reference/index.html). En primer lugar, cabe decir que db4o mantiene un contenedor de objetos, que podemos entender como una conexión a la base de datos. Todas las consultas que hacemos, las haremos contra este contenedor, el cual nos permite mantener las propiedades ACID, dado que cada contenedor tiene su propia transacción.

Un concepto importante que hay que tener en cuenta es el de identidad. Db4o mantiene la identidad de los objetos sin necesitar atributos o relaciones que los identifiquen unívocamente, de forma similar a como se mantienen los objetos en memoria normalmente. Así, si pedimos un objeto de diversas maneras, db4o siempre nos retornará el mismo objeto, la misma referencia. También hemos de saber que db4o mantiene una caché de referencias en memoria: una tabla donde las referencias a los objetos se corresponden con la representación de las mismas en disco, para poder identificar en todo momento cuál es el objeto que se ha de modificar, borrar o leer.

Finalmente, db4o implementa lo que se conoce como activation depth (profundidad de activación). Si, por el motivo que sea, no queremos que la OODB nos cargue todos los objetos persistidos en memoria, db4o nos ofrece una solución alternativa: cargar sólo aquellas partes del grafo de objetos que nos son de interés. Es decir, cuando hacemos una consulta se activarán (cargarán) aquellos objetos que hemos pedido, así como todos aquellos que estén referenciados por estos, y así de forma transitiva hasta una cierta profundidad, que es, simplemente, la cantidad de referencias que saltamos desde los objetos pedidos originalmente. Db4o nos permite configurar esta profundidad de forma global, específica para alagunas clases o automática, en cascada, según se requiera.

Lo que explicamos aquí es una mera introducción bastante superficial de db4o. Este sistema de gestión es evidentemente mucho  más complejo y queda al gusto del lector profundizar en los conceptos y el uso de esta OODB.

"Querying db4o"

Db4o soporta diversos mecanismos de consulta: queries nativas, queries basadas en ejemplo y SODA queries. Cada uno tienes sus ventajas e inconvenientes: podemos realizar fácilmente queries usando un objeto de ejemplo, pero no siempre podemos afinar los requisitos de las consultas con estos stubs. Por otra parte, las queries nativas nos permiten programarlas en el lenguaje de programación con el que implementamos la aplicación, manteniendo así la coherencia en todo el proyecto y ayudando a mejorar el mantenimiento posterior. Por último, las SODA queries nos permiten realizar consultas complejas de forma muy potente, aunque primero nos tendremos que familiarizar con esta API, ciertamente poco documentada, que básicamente, nos permite hacer recorridos por el grafo de objetos que tenemos persistidos. En este tutorial se dan unos buenos ejemplos de queries de ejemplos y queries nativas para aprender a hacer consultas básicas.

Implementando db4o

En inLab hemos implantado db4o en el proyecto SomUPC con entusiasmo después de hacer unas primeras pruebas, para ver si realmente nos era útil y sencillo llevarlo hacia adelante. Después de todo, podemos decir que ha resultado mucho más fácil implementar la capa de persistencia del proyecto con db4o que con Hibernate y una BD relacional clásica, nuestra primera elección en este sentido. Veremos algunos de los métodos que nos permiten configurar la base de datos y acceder, modificar y eliminar los objetos que guardamos.

Para abrir la base de datos podemos utilizar una clase auxiliar, que nos haga la gestión de la conexión y desconexión, así como el mantenimiento del contenedor de objetos en el cual cargamos los resultados de las consultas que hacemos:

 

public class Db4oHandler {
    private ObjectContainer db;
    public void openDatabase(String path) {
        EmbeddedConfiguration conf = Db4oEmbedded.newConfiguration();
        db = Db4oEmbedded.openFile(conf, path);
    }
    public void close() {
        try {
            db.commit(); //commit the last in-mem cached (not persisted) changes!
            db.close();
        } catch(Exception e){
            //exception handling here!
        }
    }
    public ObjectContainer getDb() {
        return db;
    }
}

 

Con estas pocas líneas podemos tener una base de datos db4o operativa y funcionando, en el fichero que definamos en el path indicado. Una vez abierta la BD; guardar un objeto de la clase Foo es tan sencillo como esto:

public class Foo {
    private String bar; //this will be stored
    private transient String temp; //this will NOT be stored (it's transient!)    
    private List<Object> list; //the list and all the objects it contains will be stored
    public Foo() {}
}

    // …

    Foo foo = new Foo();
    ObjectContainer db = dbHandler.getDb();
    db.store(foo);
    db.commit();

Y borrarlo tan simple como:

db.delete(foo); //foo was stored in the database
db.commit();

Como vemos, no hace falta mantener un código lleno de anotaciones o un complejo fichero de configuración que nos defina los mappings de los atributos de nuestras clases: sólo POJOs, fáciles de leer, modificar y mantener. El único código repetitivo (en la parte de persistencia de datos) que quedará en nuestros ficheros será aquel que el propio lenguaje o nuestra propia traza requieran. De hecho, si nos aseguramos de que la base de datos se cerrará correctamente con la aplicación, es decir, que se ejecutara la función close()  del Db4oHandler, no hará falta hacer db.commit() cada vez que se haga una operación en la BD.

db4o y Android

Finalmente, dado que es una base de datos integrada, fácilmente accesible y configurable, comprobamos que es muy útil para desplegar en aplicaciones móviles, en plataformas como Android: http://java.dzone.com/articles/using-db4o-android-application. Así podemos aprovechar el potencial de esta OOBD y la rapidez de desarrollo que nos facilita para llevar a cabo los proyectos de apps más ambiciosos que queramos.