db4o y la integridad referencial

Hace un par de semanas que he empezado a trabajar con db4o, un sistema gestor de bases de datos nativo y embebible orientado a objetos (para más detalles visita este enlace a la wiki), que sinceramente me ha gustado mucho por su flexibilidad y versatilidad, tanta que puede resultar intratable sin los conocimientos adecuados.

 1.-¿Qué sucede cuando almacenamos un objeto?

Al almacenar un objeto en db4o se observan entre otros los siguientes comportamientos, que pueden parecer anómalos si no se entiende lo que sucede:
  • No se respeta la integridad referencial, es decir podemos guardar el mismo objeto tantas veces como queramos. La explicación a esto es que cuando usamos el método store db4o crea un nuevo OID (si estuviéramos ante una BD relacional lo consideraríamos la clave primaria) y se le asigna al objeto que acabamos de guardar por este motivo es posible almacenar el mismo objeto tantas veces como queramos sin violar la integridad referencial.
  • Cuando un objeto tiene entre sus atributos referencias a otros objetos (componentes) se guardan también dichos objetos en la base de datos. Esto se debe a que al guardar un objeto compuesto db4o aplica el equivalente al método store a cada uno de los objetos componente que lo forman lo que unido a lo expuesto en el punto anterior puede dar lugar a un despilfarro de espacio en memoria muy considerable. Otra consecuencia de esto es que si por algún motivo db4o no logra guardar alguno de los objetos componente la referencia a este en el objeto compuesto tomará valor null en la BD y al recuperarlo nos puede saltar el obvio NullPointerException.

2.-¿Como crear integridad referencial?


La forma de crear un modelo de integridad referencial en nuestra aplicación es mediante la gestión de los eventos de interacción con la BD. Para esto la API de db4o pone a nuestra disposición la interfaz EventRegistry, que permite crear un gestor para los eventos relacionados con la persistencia, facilitándonos la labor de cancelar una inserción o desencadenar un borrado en cascada ante el borrado de un objeto.
En el siguiente fragmento de código se crea una conexión a la BD y se asocian unos listener a los eventos de creación y borrado tal como se ven en la imagen.
Asociación de listeners para eventos de creación y borrado de objetos

De esta forma se consigue interceptar los procesos de interacción con la BD y nos da la oportunidad de imponer nuestras reglas, tal como si de un trigger se tratase.
A continuación se muestran en una serie de imágenes las distintas partes que componen un listener de ejemplo que controla los procesos de inserción.
En primer lugar nuestra clase listener debe implementar la interfaz EventListener4<CancellableObjectEventArgs> para que podamos cancelar el proceso si queremos.

Declaración de la clase

A continuación se debe implementar el método onEvent declarado por la interfaz. En este se definen las acciones a tomar cuando se produce una inserción. Se va a mostrar despiezado el método onEvent para explicar como se tratan los distintos problemas de integridad referencial. Es importante tener en cuenta que se ha creado una variable boolean llamada ilegal que si al acabar la sección de comprobaciones del método sigue conteniendo true indica que se debe cancelar la inserción del objeto.

Comprobación de que no existe el objeto en la base de datos
En esta imagen se muestra la comprobación de la integridad referencial para un objeto de tipo usuario, que no referencia a ningún otro objeto (sin claves ajenas). Dicha comprobación consiste en verificar que no existe un objeto igual almacenado en la BD atendiendo a la definición que hayamos hecho del método equals dentro de la clase Usuario. Para esto hemos creado el método retrieveUsuario, cuyo código se muestra a continuación.
Método retrieveUsuario
A continuación se muestra la comprobación de la existencia de los objetos referenciados dentro de la base de datos, lo que serían las claves ajenas, porque en este caso se ha acordado que no puede almacenarse un objeto si no se han almacenado previamente y de forma explícita aquellos objetos a los que referencia.
Comprobación de la existencia de los objetos referenciados desde el objeto que se está almacenando.

Un detalle importante de este fragmento de código es que además de comprobar si existe el Usuario dentro de la BD asigna la referencia al que ya está almacenado para evitar que cuando se cancele la inserción del Usuario referenciado inicialmente la referencia pase a apuntar a null.

Por último solo queda comprobar si el boolean ilegal sigue siendo true y de ser así cancelar la inserción.
Cancelo la inserción si ilegal sigue siendo true


Con lo anterior queda resuelto el problema de integridad referencial.

Si a alguien le interesa descargarse el proyecto de netbeans donde desarrollo el ejemplo que esté pendiente porque en los próximos días lo subiré a esta misma entrada.

1 comentario:

  1. Muy buen aporte, espero que subas el proyecto, me interesa el tema, gracias!!

    ResponderEliminar