titomendoza
Usuario (Argentina)

Antes que nada, para los que no conozcan Db4o, es una Base de Datos, embebida, 100% orientada a objetos nativa para Java y .NET y además es de código abierto. Tanto, en tan pocas palabras Quiero aclarar, que este artículo presenta mi experiencia personal con db4o, aplicada a un proyecto hecho en Java y no propone ser propaganda ni convencerlos de que db4o es un invento divino, sino solo mostrarles una buena opción para proyectos pequeños, de escritorio, y que no requieran de un motor de base de datos grande. Bueno, vamos a lo nuestro. Leí un caso de éxito de la aplicación de db4o que quiero contarles. Fue usado como gestor de objetos en parte del sistema de gestión en tiempo real de los trenes bala en españa. Para no mantener todos esos objetos en memoria, los persisten mediante db4o, y luego, cuando es necesario replican de db4o a un motor Oracle. La ventaja de tener todos esos objetos guardados mediante db4o son muchas, la velocidad de persistencia, lo fácil y rádido que les significó implementar el framework a los desarrolladores. En mi caso, el sistema debía permitir almacenar objetos, y objetos compuestos de varios objetos más. Esto en db4o, es tan transparente como persistir el objeto principal, ya con sus objetos dentro. La implementación de db4o a nuestro proyecto es muy sencilla. Basta con crear una librería con los jar de db4o o las dll en caso de .net, agregar la librería a nuesto proyecto y listo. Para realizar cualquier acción, ya sea insert, update o querys debemos manipular una instancia de ObjectContainer. Para esto yo cree una clase Conector, la cual contiene un metodo Conectar que recibe dos parámetros: ruta y nombre del archivo de base de datos. public ObjectContainer conectar(String ruta, String nombreBase) { new File(ruta).mkdir(); return Db4o.openFile(ruta + File.separator + nombreBase); } El método la primera vez que se ejecuta crea el directorio y el archivo de la base de datos en caso de que no exista. Luego, cada vez que lo llamamos se limita a abrir la base de datos y devolvernos el ObjectContainer para manejarla. Para realizar cualquier acción sobre la base de datos hice una clase Fachada, la cual contiene tanto métodos específicos como genéricos. Aplicando el patrón singleton, Fachada contiene un constructor private que al crearse la primera instancia de ella misma en el método getInstancia, llama a un método inicializar el cual crea una instancia de Conector y llama al método conectar. La clase Fachada contiene una instancia ObjectContainer, a la cual se le asigna el ObjectContainer devuelto por el método Conectar de la clase Conector, y que a partir de ahora, será nuestro punto de contacto con la base de datos. A continuación está la clase Fachada con los métodos básicos para la conexión: public class Fachada { static Fachada instancia = null; static ObjectContainer db = null; private Fachada(){ inicializar(); } public static synchronized Fachada getInstancia(){ if ( instancia == null ) instancia = new Fachada(); return instancia; } private void inicializar(){ Conector con = new Conector(); db = con.conectar( "datos", "base.dat" ); } } Insert de Objetos Ahora bien, llegó la hora de guardar un objeto en la base de datos. Para los insert, no es necesario crear métodos específicos. Cuando digo específicos me refiero a que no es necesario un método guardarCliente(Cliente cliente), sino que con un guardar(Object obj) nos basta (comenzó la magia Chulo) public void grabar(Object obj) throws Db4oException { db.set(obj); } El método set de la clase ObjectContainer guarda un objeto en la base de datos. Como ven podemos recalcar dos cosas: Le pasamos un objeto obj de la clase Object por lo que le podemos mandar cualquier cosa, y en segundo lugar el método arroja excepciones. ¿Donde manejamos esas excepciones? Yo decidí manejarlas en el Gestor específico de cada clase, ejemplo: Método ingresar de la clase GestorCliente: public void ingresar(Cliente cli) { try { Fachada.getInstancia().grabar( cli ); } catch (Db4oException ex) { ex.printStackTrace(); System.out.println(ex.getMessage()); } } Como ven, nuestro método ingresar recibe una instancia de Cliente, la cual le manda derecho a la Fachada. A esa altura, podemos arrojar una excepcion propia que la agarre el ControladorCliente y mande un mensaje a la interfaz, un poco mas humano Update La actualización se realiza mediante el mismo método set. Método en la clase Fachada: public void actualizar(Object obj) throws Db4oException { db.set(obj); } Delete Para borrar un objeto debemos asegurarnos igual que en el caso anterior, de estar seguros que el objeto recuperado es el que queremos borrar. Luego basta con un método genérico: public void borrar(Object obj) throws Db4oException { db.delete(obj); } El único recaudo que debemos tomarnos es estar seguros que el objeto que le pasamos, el mismo que recuperamos anteriormente, que modificamos y que queremos persistir esos cambios. En mi caso, cuando recupero el objeto que quiero modificar, ese mismo objeto lo asigno a una instancia de Cliente por ejemplo que tengo en memoria, levanto los datos a la UI, y luego cuando quiero aceptar los cambios, actualizo la instancia con los datos de la UI y llamo al método actualizar, así de simple. Para asegurarme de tratar el objeto indicado, a cada objeto que persisto le genero un UID. Consultas Db4o ofrece varias formas de realizar consultas, yo les explicaré la que apliqué yo a mi proyecto. En la clase Fachada hice un método genérico que recibe un Objeto y devuelve un Vector de esa misma clase, con los resultados de la consulta. Para buscar un cliente por ejemplo, debemos armar una instancia de Cliente prototipo y luego db4o nos trae lo que es igual o se parece a nuestro prototipo mediante el método get. Entonces el método buscar genérico queda así: public <T> Vector<T> buscar(T proto) throws Db4oException { Vector<T> resultado = new Vector<T>(); ObjectSet<T> objs = db.get( proto ); while(objs.hasNext()) { resultado.add(objs.next()); } return resultado; } Si nosotros le pasamos al método una instancia de Cliente con el nombre "Juan" y los demás campos en blanco, db4o nos devuelve un ObjectSet de Clientes, con los Clientes que tengan su atributo nombre con el valor "Juan". Este ObjectSet, para comodidad nuestra, conviene pasarlo a un Vector de Clientes. Esto se hace en el bucle while. Este es el tipo de búsqueda mas simple, pero es muy útil para búsquedas sencillas, o cuando solo queremos listar determinados objetos de la base de datos. Consultas específicas: En el caso de que por ejemplo necesitamos buscar clientes, filtrando solo por nombre y apellido, y queramos que nos arroje en forma ordenada los resultados que son parecidos, debemos hacer un método específico para esto, ya veremos por que. public Vector<Cliente> buscarClientes(Cliente cli) { Vector<Cliente> resultado = new Vector<Cliente>(); Query consulta = db.query(); consulta.constrain(Cliente.class); Constraint constr = cons.descend("apellido".constrain(cli.getApellido()).like(); Constraint constr2 = cons.descend( "nombre" ).constrain(cli.getNombre()).like(); cons.descend( "apellido" ).orderAscending(); ObjectSet<Cliente> clientes = cons.execute(); while(clientes.hasNext()) { resultado.add(clientes.next()); } return resultado; } El método buscarCliente también recibe un objeto prototipo, pero específico de la clase Cliente. Esto lo hacemos, porque nosotros necesitamos filtrar por campos específicos de la clase Cliente, como son nombre y apellido, y además para esta forma de consulta db4o necesita que le digamos porque campos queremos filtrar. Primero creamos una instancia de la clase Query, a la que llamamos consulta. A esta consulta le decimos antes que nada que vamos a filtrar Clientes, mediante el método constrain. Luego creamos dos instancias de la clase Constraint, constr y constr2. A cada una de ellas le declaramos que queremos filtrar por el campo apellido y nombre respectivamente, y queremos que nos arroje los esultados parecidos, no solo las coincidencias exactas. Mediante el método orderAscending estamos aclarando que queremos que ordene el resultado de la consulta por apellido en forma ascendente. Cargamos el Vector de Clientes con los Clientes del ObjectSet resultado y lo devolvemos. Si por ejemplo nuestra clase Pedido tiene dentro un vector de Productos, y queremos buscar un Pedido sabiendo el número o nombre de un producto, la consulta es sencilla: basta con crear una instancia prototipo de Producto, cargarle los atributos que conocemos sus valores, luego crear una instancia prototipo de Pedido, asignarle el Producto recién creado y llamar al método buscar genérico o específico pasandole el Pedido como parámetro. public Vector<Produccion> buscarPedido(Pedido ped){ Vector<Pedido> resultado = new Vector<Pedido>(); Query cons = db.query(); cons.constrain( Pedido.class ); if ( ped.getCodigo() != null ){ Constraint constr = cons.descend( "codigo" ).constrain(ped.getCodigo()).like();} if(ped.getCliente() != null ){ Constraint constr2 = cons.descend( "cliente" ).constrain(ped.getCliente()).equal();} ObjectSet<Produccion> pedidos = cons.execute(); while(pedidos.hasNext()) { resultado.add(pedidos.next()); } return resultado; } El método anterior muestra una idea nueva. Por ejemplo, antes de asignar un Constraint a la consulta, preguntamos en el if si ese dato lo tiene el prototipo o no, ya que si no lo tiene y buscamos, nos va a devolver objetos que no tengan ese atributo cargado, y nuestro caso puede ser que no lo sepamos, entonces lo mandamos en blanco. Preguntando si esta cargado o no, nos evitamos ese problema. El caso del Constraint de cliente es por equal (igual). Con todos estos métodos presentados ya tenemos para armarnos una Fachada básica y comenzar a experimentar con db4o. Bueno, eso es todo por ahora, fue solo un pantallazo general de todas las posibilidades que nos ofrece db4o. En un próximo artículo podemos entrar mas en detalle en cada cosa. PD: casi me olvido, para cerrar la conexión se usa el método close() de la clase ObjectContariner: public void desconectar() { db.close(); } Si no lo cierra, igualmente debería cerrarse automáticamente, pero a veces no lo hace Indeciso Ahora si, ¡saludos para todos! DESCARGA Db4o: http://developer.db4o.com/files/folders/db4o_61/default.aspx (Ahi tienen las librerias para Java y .NET) Object Manager: http://developer.db4o.com/files/folders/objectmanager_64/default.aspx dRS: http://developer.db4o.com/files/folders/drs_64/default.aspx