Skip to content

Instantly share code, notes, and snippets.

@jorgeteixe
Last active March 2, 2020 08:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jorgeteixe/9f6237f536617a91d5884c0046b9a709 to your computer and use it in GitHub Desktop.
Save jorgeteixe/9f6237f536617a91d5884c0046b9a709 to your computer and use it in GitHub Desktop.
Apuntes AAD

Bases de datos orientadas a objetos

Sistema Gestor de Base de Datos Orientada a Objetos (SGBDOO).

¿Estudias con mis apuntes?

Características

  • Los datos se almacenan como objetos.
  • Cada objeto se identifica con un identificador único no modificable.
  • Cada objeto define métodos, atributos e interfaz de acceso.

Características según Malcolm Atkinson en 1989

  • Debe soportar objetos complejos.
  • Los objetos deben tener un identificador independiente de los valores de los atributos.
  • Debe soportar encapsulamiento.
  • Debe soportar tipos o clases.
  • Debe soportar herencia.
  • Debe soportar sobrecarga.
  • El lenguaje de manipulación (DML) debe de ser completo.
  • Los tipos de datos deben ser extensibles.
  • Debe soportar la persistencia de datos.
  • Debe soportar grandes bases de datos.
  • Debe soportar concurrencia.
  • Debe ser capaz de recuperarse ante fallos de hardware y software.
  • Debe proporcionar un método de consulta sencillo.

Ventajas de los SGBDOO

  • Mayor capacidad de modelado.
  • Extensibilidad.
  • Única interfaz entre el lenguaje de manipulación y el lenguaje de programación, evitando SQL incrustado.
  • Lenguaje de consultas más expresivo.
  • Soporte para transacciones largas.
  • Adecuación a aplicaciones avanzadas de bases de datos.

Inconvenientes de los SGBDOO

  • Falta un modelo de datos universal.
  • Falta de experiencia de uso.
  • Insuficiencia de estándares.
  • Competencia con gran experiencia.
  • La optimización de consultas compromete la encapsulación.
  • Complejidad.
  • Falta de soporte para vistas.
  • Falta de soporte para seguridad.

El estándar ODMG

El Object Database Management Group es un grupo de fabricantes de SGBDOO con el objetivo de definir estándares.

La última versión del estandar ODMG 3.0 propone los siguientes componentes.

Componentes

  • Modelo de objetos.

Características principales:

  • Todos los objetos tienen un identificacor único OID (Object ID).

  • Los literales no lo tienen, no son objetos, aparecen embebidos en objetos.

  • Lenguaje de definición de objetos (Object Definition Language).
  • Lenguaje de consulta de objetos (Object Query Language).
  • Conexión con los lenguajes C++, Smalltalk y Java.

Tipos de objetos

  • Atómicos: boolean, short, long, enum, string, etc.
  • Tipos estructurados: date, time, timestamp, interval.
  • Colecciones: set, bag, list, array, dictionary.

Los literales pueden ser atómicos, colecciones, estructuras o tomar el valor NULL.

El lenguaje de consultas OQL

Object Query Language es el lenguaje de consultas estándar.

Características

  • Orientado a objetos.
  • Declarativo del tipo SQL, sintaxis similar a SQL.
  • Acceso declarativo a propiedades y métodos.
  • Semántica formal bien definida.
  • No incluye operaciones de actualización. Las modificaciones se realizan directamente a los objetos.
  • Dispone de operaciones de colecciones: min, max, count, etc.

Sintaxis básica

La sintaxis básica es una estructura SELECT como en SQL.

SELECT valores
FROM colecciones
[WHERE condición]

En la cláusula FROM se suele utilizar variables iterador que se pueden especificar de varias formas:

FROM Clientes x
FROM x IN Clientes
FROM Clientes AS x

Para acceder a atributos y objetos se utilizan expresiones de camino. Se utiliza mediante puntos:

SELECT x.nombre FROM x IN Clientes WHERE x.sexo="F"

Ejemplos más avanzados:

SELECT v.IDVENTA FROM Ventas v
SELECT v.fecha_venta FROM Ventas v
SELECT v.total_venta() FROM Ventas v
SELECT v.cliente.nombre FROM Ventas v  -- cliente es un literal, al que podemos acceder
--------------------------------------
-- INCORRECTO
SELECT v.productos FROM Ventas v       -- productos es una colección.
-- CORRECTO
SELECT p.nombre, p.precio FROM Ventas v, v.productos p WHERE v.IDVENTA = 1

También se pueden utilizar alias, distinct y order by:

-- alias: nombre_cliente >> v.cliente.nombre
SELECT nombre_cliente:v.cliente.nombre FROM Ventas v
-- distinct: no saca nombres duplicados
SELECT DISTINCT nombre_cliente:v.cliente.nombre FROM Ventas v
-- order by: ordenado por nombre
SELECT nombre_cliente:v.cliente.nombre FROM Ventas v ORDER BY nombre_cliente ASC

Operadores de comparación

Los operadores básicos son:

= != < <= > >=

Para comparar cadenas de caracteres se utiliza:

IN

LIKE -> Permite utilizar '_', '?', '%' y '*'.

Ejemplos:

-- Personas de 'TOLEDO' con apellido que empieza por 'A'.
SELECT p.nombre, p.apellido1
FROM Personas p
WHERE p.ciudad = "TOLEDO"
AND p.apellido1 LIKE "A%"

-- Personas cuya ciudad contenga la 'J'.
SELECT p.nombre, p.ciudad
FROM Personas p
WHERE 'J' in p.ciudad

Cuantificadores y operaciones sobre colecciones

Véase con ejemplos:

-- FOR ALL
-- Todos los objetos de la colección deben cumplir (todos los productos < 20 €):
SELECT v.IDVENTA
FROM Ventas v
WHERE FOR ALL p IN v.productos : p.precio < 20

-- EXISTS con que alguno cumpla llega
-- UNIQUE solo uno cumpla

-- Operadores AVG, SUM, MIN, MAX, COUNT
AVG(SELECT p.precio FROM Ventas v, p v.productos WHERE v.IDVENTA = 1)

Ejemplo de BDOO - Neodatis

Neodatis Object Database es una BDOO de código abierto que funciona con Java, .Net, Groovy y Android.

Operaciones básicas

Para abrir una base de datos utilizamos el método open() de ODBFactory que devuelve un ODB.

ODB odb = ODBFactory.open("neodatis.test"); // Abrir BD

Para almacenar objetos utilizamos el método store() de ODB.

Jugador j = new Jugador("Jorge", "Béisbol", "Redondela", 19);
odb.store(j);

Para recuperar los objetos utilizamos el método getObjects().

Objects<Jugador> objects = odb.getObjects(Jugador.class);
while(objects.hasNext()) {
    Jugador j = objects.next();
    System.out.println(j.toString());
}
odb.close() // para cerrar la conexión y validar los cambios hay que hacer .close()

Para acceder a objetos mediante su OID.

OID oid = OIDFactory.buildObjectOID(3); // Obtener OID 3
Jugador j = (Jugador) odb.getObjectFromId(oid);

// Otras formas de conseguir el OID
OID oidOtroJugador = odb.store(otroJugador);
OID oidOtroMas = odb.getObjectId(otroMas);

Consultas sencillas

Para realizar las consultas utilizamos la clase CriteriaQuery:

IQuery query = new CriteriaQuery(Jugador.class, Where.equal("deporte", "tenis"));
query.orderByAsc("nombre,edad");
Objects<Jugador> objects = odb.getObjects(query);
System.out.println(objects.size() + " jugadores.");

// Para solo coger el primero
Jugador j = (Jugador) odb.getObjects(query).getFirst();
System.out.println(j.toString());

Para modificar un objeto lo primero es recuperarlo de la BDOO, y a continuación restaurarlo con el método store(). Recordemos que para validar cualquier cambio hay que ejecutar close().

Vamos a cambiar el deporte de 'María' a 'baloncesto':

query = new CriteriaQuery(Jugador.class, Where.equal("nombre", "María"));
Jugador jMaria = (Jugador) odb.getObjects(query).getFirst(); // cogemos el primero
jMaria.setDeporte('baloncesto'); // modificamos el objeto
odb.store(jMaria); // mandamos los cambios
odb.close(); // hacemos close para validar los cambios

Para eliminar un objeto lo localizamos como a 'María' en el ejemplo anterior y luego hacemos delete().

query = new CriteriaQuery(Jugador.class, Where.equal("nombre", "María"));
Jugador jMaria = (Jugador) odb.getObjects(query).getFirst();
odb.delete(jMaria);
odb.close();

Para realizar la consulta podemos ayudarnos de la interfaz ICriterion, por ejemplo, esto nos permite reutilizar el mismo criterio en diferentes consultas de distintas clases.

ICriterion criterio = Where.equal("nombre", "Francisco");
CriteriaQuery query1 = new CriteriaQuery(Jugador.class, criterio); // Query en Jugador
Objects<Jugador> jugadoresFrancisco = odb.getObjects(query1);
CriteriaQuery query2 = new CriteriaQuery(Entrenador.class, criterio); // Ahora Entrenador
Objects<Entrenador> entrenadoresFrancisco = odb.getObject(query2);

Otros métodos de Where:

Where.like("nombre", "N%"); // nombre empieza por N
Where.gt("edad", 14); // edad mayor que 14
Where.lt("precio", 50); // precio menor que 50
Where.ge("edad", 18); // edad mayor o igual que 18
Where.le("precio", 5); // precio menor o igual que 5
Where.contain("productos", prod); // contiene tal producto
Where.isNull("atributo"); // dicho atributo es nulo
Where.isNotNull("attr"); // attr tiene un valor no nulo

Y para expresiones lógicas utilizamos las clases And, Or y Not. Para unir las condiciones utilizamos add():

ICriterion criterio = new And().add(Where.equal("nombre", "María"))
    						   .add(Where.ge("edad", 18));

criterio = new Or().add(Where.equal("ciudad", "Vigo"))
                   .add(Where.equal("empleo", "programador"));

criterio = Where.not(Where.like("nombre", "A%"));

Consultas más complejas

Para utilizar agrupamientos GROUP BY y las funciones de grupo SUM, MAX, etc., usamos API Object Values de Neodatis, que nos provee acceso a los atributos de los objetos. Ejemplo:

ValuesCriteriaQuery crit = new ValuesCriteriaQuery(Jugador.class)
                                     .field("nombre")
                                     .field("deporte");
Values valores = odb.getValues(crit);
while(valores.hasNext()) {
    ObjectValues v = (ObjectValues) valores.next();
    System.out.println("Nombre: " + v.getByAlias("nombre") + "\n" +
                       "Deporte: " + v.getByIndex(1));
}

Como vemos en el ejemplo, podemos recuperar los valores por su nombre o por el índice que ocupan utilizando los respectivos métodos. Pero también podemos asignarle un alias a cada atributo de la siguiente forma:

ValuesCriteriaQuery crit = new ValuesCriteriaQuery(Jugador.class)
                                     .field("nombre", "n") // Alias n para nombre
                                     .field("deporte");
Values valores = odb.getValues(crit);
while(valores.hasNext()) {
    ObjectValues v = (ObjectValues) valores.next();
    System.out.println("Nombre: " + v.getByAlias("n") + // recuperamos por alias
                     "\nDeporte: " + v.getByIndex(1));
}

Algunos ejemplos de las funciones de grupo como count() o avg():

Values v = odb.getValues(new ValuesCriteriaQuery(Jugador.class).count("nombre", "cant"));
ObjectValues ov = v.nextValues();
int cantidad = ov.getByAlias("cant").intValue(); // count devuelve BigInteger
                                                 // con .intValue() conseguimos el int

v = odb.getValues(new ValuesCriteriaQuery(Jugador.class)
                 .max("edad", "max_edad")
                 .min("edad", "min_edad")
                 .avg("edad", "avg_edad")
                 .sum("edad", "sum_edad"));
ov = v.nextValues(); // Devuelven BigDecimal, que con los métodos finales, convertimos.
int maxmimo = ov.getByAlias("max_edad").intValue(); 
int minimo = ov.getByIndex(1).longValue();
float media = ov.getByAlias("avg_edad").floatValue();
long suma = ov.getByAlias("sum_edad").longValue();

Ejemplo de uso de la sentencia GROUP BY:

Values groupby = odb.getValues(new ValuesCriteriaQuery(Jugador.class)
                              .field("ciudad")
                              .count("nombre", "cant")
                              .groupBy("ciudad")); // obtenemos los jugadores por ciudad
while(groupby.hasNext()) {
    ObjectValues ob = (ObjectValues) groupby.next();
    System.out.println(ob.getByIndex(0) + ": " + ob.getByAlias("cant"));
}

Y por útlimo cuando un objeto tiene una relación con otro, podemos acceder a sus atributos utilizando puntos como en el ejemplo:

Values v = odb.getValues(new ValuesCriteriaQuery(Jugador.class, 
                                                 Where.equal("pais.nombrecorto", "ES"))
                        .field("nombre")
                        .field("coche.matricula"));

Modo cliente/servidor

Neodatis también puede ser utilizado como una base de datos cliente/servidor, para configurarla utilizamos:

ODBServer server = ODBFactory.openServer(8000); // siendo 8000 el puerto 

server.addBase("base", "neodatis.test"); // siendo "base" el nombre que utilizarán los 
                                         // clientes para referirse a la base de datos
                                         // llamada "neodatis.test"
server.startServer(true); // para iniciar el servidor
                          // funciona como un subproceso en segundo plano

Desde el cliente, si está en la misma máquina, tan solo utilizamos:

ODB odb = server.openClient("base"); // abre la bd "neodatis.test" asociada a "base"

Si accedemos desde otro equipo:

ODB odb = ODBFactory.openClient("localhost", 8000, "base");

IMPORTANTE

  • Las clases que se vayan a utilizar en la base de datos tienen que estar añadidas como ficheros a parte en el proyecto Java y tienen que implementar la interfaz Serializable.

  • Recordar, en Neodatis es importante llamar al método close() para confirmar los cambios.

@andrespervil
Copy link

andrespervil commented Mar 2, 2020

Me gustan las 8==============D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment