No es un bug, es una característica no documentada

domingo, 6 de marzo de 2016

Acceso a datos. Neodatis

2:37 Posted by Inazio Claver , 16 comments
Neodatis ODB es una base de datos orientada a objetos con licencia GNU muy simple que actualmente corre en los lenguajes Java, .Net, Google Android, Groovy y Scala.

Con Neodatis podemos evitar la falta de impedancia entre los mundos orientados a objetos y los relacionales, ya que actúa como una capa de persistencia transparente para Java, .Net y Mono.

Neodatis ODB soporta consultas nativas, es decir, podemos lanzar una consulta directamente desde Java, por ejemplo.
Además es bastante simple e intuitivo. Los objetos pueden ser añadidos fácilmente a la base de datos, lo que requiere clases no repetitivas y que las clases ya existentes no puedan modificarse.


También cuenta con un explorador ODB, una herramienta gráfica para navegar, consultar, actualizar y borrar objetos, así como la importación / exportación de bases de datos desde y hacía archivos XML.


Sintaxis

La sintaxis para trabajar con Neodatis vamos a verla en Java. Desconozco como funciona en otros lenguajes como .Net o Groovy, por ejemplo, pero nos servirá para hacernos una aproximación a esta base de datos.

Lo primero, debemos tener en cuenta que es muy importante que después de haber abierto una conexión a la base de datos y realizar las operaciones que deseemos, cerremos dicha conexión, si no nos fallará en futuras ocasiones.

Conexión / Desconexión


La conexión la realizamos con un objeto de clase ODB, en la que indicaremos la ruta donde tenemos nuestra base de datos. Esto servirá tanto para abrirla como para crear una nueva, en caso de que en la ruta indicada aún no exista ninguna. En mi caso, vamos a generar una nueva en la ruta D:\ y se llamará neodatis.test.

ODB odb = ODBFactory.open("d:\\neodatis.test");

Y para cerrar la conexión, simplemente indicaremos que queremos cerrar el objeto ODB creado.

odb.close();

Inserción de objetos

No necesitamos definir en la base de datos como queremos guardar los objetos, éstos lo harán automáticamente, creándose las “tablas” respecto a las clases que vayamos almacenando. Lo que sí deberemos tener creada es una clase en Java (en mi caso) que indique las propiedades y métodos a guardar en la base de datos. Es decir, si queremos almacenar un registro de jugadores de varios deportes, deberemos crear una clase Jugadores, con sus propiedades, métodos, getters y setters correspondientes. Tal que así, por ejemplo.

public class Jugadores {
     
      // Propiedades
      private String nombre;
      private String deporte;
      private String ciudad;
      private int edad;
     
      // Constructores
      public Jugadores(){};
     
      public Jugadores(String nombre, String deporte, String ciudad, int edad) {
            super();
            this.nombre = nombre;
            this.deporte = deporte;
            this.ciudad = ciudad;
            this.edad = edad;
      }
     
      // Métodos
      public String getNombre() {
            return nombre;
      }
      public void setNombre(String nombre) {
            this.nombre = nombre;
      }
      public String getDeporte() {
            return deporte;
      }
      public void setDeporte(String deporte) {
            this.deporte = deporte;
      }
      public String getCiudad() {
            return ciudad;
      }
      public void setCiudad(String ciudad) {
            this.ciudad = ciudad;
      }
      public int getEdad() {
            return edad;
      }
      public void setEdad(int edad) {
            this.edad = edad;
      }
} // Fin clase Jugadores

Creada la clase, la siguiente fase es generar los objetos Jugadores que queramos almacenar en la BD e insertarlos. Tambíen bastante intuitivo.

// Creo los jugadores
Jugadores j1= new Jugadores ("María", "voleibol", "Madrid", 14);
Jugadores j2= new Jugadores ("Miguel", "tenis", "Madrid", 15);
Jugadores j3= new Jugadores ("Mario", "baloncesto", "Guadalajara", 15);
Jugadores j4= new Jugadores ("Alicia", "tenis", "Madrid", 14);
// Inserto los objetos
odb.store(j1);
odb.store(j2);
odb.store(j3);
odb.store(j4);

Y listo. Neodatis detectará que tipo de objeto son y los insertará en una u otra “tabla”.

Mostrar datos

Para mostrar los datos crearemos un conjunto de objetos genéricos parametrizado al tipo de objeto que queremos traernos. En este caso, Objects<Jugadores>. En él cargaremos todos los objetos del tipo Jugadores que nos traeremos desde la base de datos.
Posteriormente recorreremos el conjunto hasta el final y accederemos a las propiedades que queramos imprimir con los getters anteriormente configurados.

// Genero un conjunto de objetos y los traigo del ODB conectado
Objects<Jugadores> objects=odb.getObjects(Jugadores.class);

// Imprimo cuantos objetos me he traido de la BD
System.out.println(objects.size() + " jugadores:");

int i = 1; // Meramente estético. Así muestra listados los objetos

// Mientras haya objetos, los capturo y muestro
while(objects.hasNext()){

      // Creo un objeto Jugadores y almaceno ahí el objeto
      Jugadores jug= objects.next();
     
      // Imprimo las propiedades que me interes de ese objeto
      System.out.println((i++) + "\t: " + jug.getNombre() + "*" + jug.getDeporte() + "*" + jug.getCiudad() + "*" + jug.getEdad());
} // Fin del While

Consultas


Claro, puede darse el caso de que no queramos listar todos los objetos de un tipo determinado, sino que queramos sólos los que tengan X propiedad o cumplan tal condición.

Para ello deberemos crear un objeto IQuery, que a su vez será un objeto CriteriaQuery, donde recibirá dos parámetros. El primero será el tipo de objeto que queramos consutar, y el segundo la condición impuesta. Un Where en toda regla, básicamente.

Hecho esto, nos traeremos todos los objetos igual que en ejemplo anterior, pero pasando como parámetro del ODB nuestra consulta. Y ya imprimiríamos los resultados como vimos en el anterior punto.

Veamos un ejemplo

/* Genero la consulta. Llamo a la clase Jugadores
 * La condición será que la propiedad deporte sea igual a tenis
 */
IQuery query = new CriteriaQuery(Jugadores.class, Where.equal("deporte", "tenis"));

// Y ya, por capricho persona, ordeno el resultado por nombre y edad
query.orderByDesc("nombre,edad");

// Cargo los objetos pasando como parámetro del odb nuestra consulta
Objects<Jugadores> objects = odb.getObjects(query);

También podemos separar este proceso un poco más, por si nos resulta más claro verlo de otra forma. El único cambio que haremos será crear un objeto ICriterion donde realizaremos la consulta, y pasaremos ese objeto como segundo parámetro de nuestro objeto CriteriaQuery. Tal que así.

// Realizo la condición sobre la propiedad edad
ICriterion criterio = Where.equal("edad",14);

/* Creo el objeto para realizar la consulta y mando el ICriterion
 * como segundo parámetro
 */
CriteriaQuery query = new CriteriaQuery(Jugadores.class, criterio);

// Y cargo todo en un objeto, como siempre
Objects<Jugadores> objects = odb.getObjects(query);

Y tenemos varias formas de lanzar un where. Por ejemplo

// Jugadores que empiezan por M
ICriterion criterio2 = Where.like("nombre","M%");

// Jugadores mayores de 14
// Where.ge --> mayor o igual, Where.lt --> menor que, Where.le ---> menor o igual
            // Where.Not --> Distinto
            //Where.isNull("atributo") si un atributo es nulo
            //Where.isNotNull("atributo") si un atributo es nulo
ICriterion criterio3 = Where.gt("edad",14);

Hago un inciso para aclarar la consulta anterior. Where.gt es para indicar mayor que. También podemos usar :
  • Where.gt à mayor o igual
  • Where.lt à menor que
  • Where.le à menor o igual
  • Where.not à Distinto a
  • Where.isNull(“atributo”) si un atributo es nulo
  • Where.isNotNull(“atributo”) si un atributo es no nulo

// Jugadores que no empiezan por M
ICriterion criterio4 = Where.not(Where.like("nombre","M%"));

// Jugadores de 15 años de Madrid
ICriterion criterio = new And().add(Where.equal("ciudad", "Madrid")).add(Where.equal("edad",15));

// Jugadores >= de 15 años y de Madrid
ICriterion criterio2 = new And().add(Where.equal("ciudad", "Madrid")).add(Where.ge("edad",15));
Y también podemos realizar consultas complejas, todo lo que queramos complicarnos es posible. Las voy a pasar un poco rápido porque esto es una breve aproximación.

Por ejemplo, veamos las siguientes:

// Suma de edades
Values val = odb.getValues(new ValuesCriteriaQuery(Jugadores.class).sum("edad"));
ObjectValues ov= val.nextValues();
BigDecimal value = (BigDecimal)ov.getByAlias("edad");
// también valdría BigDecimal value = (BigDecimal)ov.getByIndex(0);

// Cuenta de jugadores
Values val2 = odb.getValues(new ValuesCriteriaQuery(Jugadores.class).count("nombre"));
ObjectValues ov2= val2.nextValues();
BigInteger value2 = (BigInteger)ov2.getByAlias("nombre");

// Media de edades
Values val3 = odb.getValues(new ValuesCriteriaQuery(Jugadores.class).avg("edad"));
ObjectValues ov3= val3.nextValues();
BigDecimal value3 = (BigDecimal)ov3.getByAlias("edad");

// Edad máxima y mínima
Values val4 = odb.getValues(((ValuesCriteriaQuery) new ValuesCriteriaQuery(Jugadores.class.max("edad", "edad_max")).min("edad", "edad_min"));
ObjectValues ov4= val4.nextValues();
BigDecimal maxima = (BigDecimal)ov4.getByAlias("edad_max");
BigDecimal minima = (BigDecimal)ov4.getByAlias("edad_min");

Borrado de objetos

Para borrar un objeto crearemos un objeto IQuery para traernos a memoria los objetos a borrar. Nos posicionamos en el primero objeto y lo borramos usando el ODB.

// Hacemos la consulta para borrar a María
IQuery query = new CriteriaQuery(Jugadores.class, Where.equal("nombre", "María"));

// Cargamos los objetos que coincidan con esa consulta
Objects<Jugadores> objects = odb.getObjects(query);

// Nos posicionamos en el primer resultado
Jugadores jug=(Jugadores) odb.getObjects(query).getFirst();

// Y lo borramos
odb.delete(jug);

Modificación de objetos

Como viene siendo habitual, creamos con un IQuery con los objetos que queramos modificar, cargaremos el objeto en la clase Jugadores de Java, le cambiaremos la propiedad que deseemos usando los setters que programamos al principio y volveremos a almacenar el objeto en la base de datos.

IQuery query = new CriteriaQuery(Jugadores.class, Where.equal("nombre", "María"));

// A María le ponemos Tiro de barra aragonesa como deporte
Objects<Jugadores> objects = odb.getObjects(query);
Jugadores jug=(Jugadores) odb.getObjects(query).getFirst();
jug.setDeporte("Tiro de barra Aragonesa");
odb.store(jug);

Instalación y configuración

¿Ya dije que íbamos a trabajarla con Java, cierto? Bien, la buena noticia es que instalarla es lo más fácil que hay. Basicamente que no requiere una instalación. Simplemente nos descargamos el paquete NeoDatis ODB de la siguiente dirección:

https://sourceforge.net/projects/neodatis-odb/

Y descomprimimos el paquete. Nos encontraremos con lo siguiente:


De esta descarga nos interesan dos archivos. Neodatis-odb-1.9.30.689.jar, que será la librería para nuestro proyecto en Java, y odb-explorer, para usar el explorador de base de datos. .bat si usamos Windows, .sh para Linux.

Yo voy a usar el IDE Eclipse, pero emplea el que te sientas más comodo.
Genera un nuevo proyecto Java, y crea una nueva carpeta, llamada lib, dentro de éste. Aquí copiaremos el .jar de NeoDatis.


Nos iremos a Propiedades del proyecto à Java Build Path à Libraries y elegiremos Add JAR, desde donde añadiremos el .jar de la ruta nombreProyecto/lib que hemos creado.


Y esa es toda la configuración previa que debemos tener. Naturalmente, ahora toca crear la estructura de clases para leer / insertar / modificar / borrar elementos de nuestra base de datos, tal y como explicamos en la sección anterior.

Y listo, ya podemos trabajar con NeoDatis ODB .

16 comentarios:

  1. Hola,

    Primero gracias por compartir tus conocimientos y ayudarnos un poco! Me gustaría hacer una consulta doble, por ejemplo buscar los jugadores de Madrid y 15 años pero en vez de hacerlo con ICreterion, hacerlo con un IQuery. ¿Es posible? Solo añadiendo otro .add?

    Gracias por tu tiempo!

    ResponderEliminar
    Respuestas

    1. Hola.
      Cuando quieres hacer una consulta doble, o de varios campos, tienes a tu disposición los métodos and() y or(), depende de la necesidad de tu condición, y posteriormente sí, simplemente tienes que añadir otro add.
      Un ejemplo de como crearlo es el siguiente:

      OR:
      IQuery query = new CriteriaQuery(Player.class, Where.or().add(Where.equal("equipo", "Madrid")).add(Where.like("edad", 15)));

      AND:
      IQuery query = new CriteriaQuery(Player.class, Where.and().add(Where.equal("equipo", "Madrid")).add(Where.like("edad", 15)));

      Un saludo :)

      Eliminar
  2. Hola buenas tardes, algun ejemplo de como aplicar neodatis para guardar informacion en una web con jsp?

    ResponderEliminar
    Respuestas
    1. Con JSP nunca lo he probado, pero haciendo una búsqueda rápida no he encontrado mucha información. Lo más parecido ha sido este ejemplo de Spring con NeoDatis:
      https://github.com/jbellmann/neodatis-springextension

      De todas formas, encuentro un poco complejo montarlo con Neodatis por la forma que tiene de manejar las conexiones. Te recomiendo que le pegues un vistazo a MongoDB para ver si cuadra más con la idea que quieres llevar a cabo.

      Eliminar
  3. Me encargaron hacer una Jframe con botones , pero aun no se como programar los botones con neodatis,

    ResponderEliminar
    Respuestas
    1. Hola Itzair. Creo que no acabo de entender tu problema. Neodatis es una base de datos orientada a objetos, no tiene nada que ver con el diseño de aplicaciones en cuanto a su parte gráfica (JFrame, JButtons, etc.). ¿Puedes explicarme a que te refieres?

      Eliminar
  4. Hola, me ha parecido muy clara esta información, no tengo nada de experiencia en bases de datos orientadas a objetos y me gustaría saber como funciona el concepto de composición en neodatis?. Me refiero a que si es que guardo un objeto compuesto por otros objetos estos se almacenan automáticamente o hay que hacerlo manualmente de alguna forma?. Espero haber sido claro con mi pregunta. Gracias de antemano.

    ResponderEliminar
    Respuestas
    1. Hola! Gracias por tu comentario.
      No sé si entiendo muy bien a que te refieres.
      Pongamos que tienes en Neodatis una base de datos de objetos Motor y Coche.
      En tu programa tienes un una clase Coche que contiene una clase Motor.
      Cuando almacenas la clase Coche, en la base de datos el registro solo se incluirá en la tabla de Coche (aunque dentro de ese registro el objeto tenga otro que sea Motor). La tabla Motor no tendrá ningún contenido a no ser que lo agregues tu igual que hiciste con Coche.

      Espero que te aclare algo mi respuesta.
      Saludos!

      Eliminar
    2. precisamente esa era mi pregunta, gracias!

      Eliminar
    3. Sin embargo acabo de comprobarlo, y sí se guardan automáticamente. Lo probé con tu ejemplo del Coche y el Motor. Creé 3 coches con sus respectivos motores y al guardar los objetos Coche también se guardaron sus respectivos objetos Motor de manera automática.
      Para comprobarlo recuperé de la base de datos todos los objetos de tipo Motor (A estos objetos no los he guardado explícitamente sino que debieron haberse guardado al almacenar el objeto Coche) y efectívamente despues de consultar a la base de datos de esta manera Objects objects = odb.getObjects(Motor.Class); obtube los objetos motor de cada Coche que almacené.

      Eliminar
    4. Ah, si? Pues pensaba que era justo lo contrario. Muchas gracias por la aclaración. Me facilitara la vida en las aplicaciones.

      Saludos!

      Eliminar
  5. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  6. Permite clave primaria, ajena etc?

    ResponderEliminar
  7. Cómo puedo importar o exportar la base de datos en xml

    ResponderEliminar