Sei sulla pagina 1di 10

Base de datos de objetos ObjectDB

ObjectDB es un poderoso sistema de administración de bases de datos orientadas


a objetos (ODBMS). Es compacto, confiable, fácil de usar y extremadamente
rápido. ObjectDB proporciona todos los servicios de gestión de bases de datos
estándar (almacenamiento y recuperación, transacciones, gestión de bloqueos,
procesamiento de consultas, etc.) pero de una manera que facilita el desarrollo
y agiliza las aplicaciones.

Características principales de la base de datos ObjectDB


 Sistema de gestión de bases de datos orientado a objetos (ODBMS) Java
100% puro.

 Sin API propietaria, administrada solo por API Java estándar (JPA 2 / JDO
2).

 Extremadamente rápido, más rápido que cualquier producto competitivo de


JPA / JDO.

 Adecuado para archivos de base de datos que van desde kilobytes a


terabytes.

 Admite tanto el modo Cliente-Servidor como el modo Integrado.

 Solo JAR sin dependencias externas.

 La base de datos se almacena como un solo archivo.

 Capacidades avanzadas de consulta e indexación.

 Eficaz en entornos de múltiples usuarios cargados.

 Se puede incrustar fácilmente en aplicaciones de cualquier tipo y tamaño.

 Probado con Tomcat, Jetty, GlassFish, JBoss y Spring.

Definición de una clase de


entidad JPA
Para poder almacenar Pointobjetos en la base de datos usando JPA,
necesitamos definir una clase de entidad . Una clase de entidad JPA es una
clase POJO (Plain Old Java Object), es decir, una clase Java ordinaria que está
marcada (anotada) como que tiene la capacidad de representar objetos en la
base de datos. Conceptualmente, esto es similar a las clases serializables, que
están marcadas como que tienen la capacidad de ser serializadas.

La clase de entidad de punto


La siguiente Pointclase, que representa puntos en el plano, está marcada
como una clase de entidad y, en consecuencia, proporciona la capacidad de
almacenar Pointobjetos en la base de datos y recuperar Pointobjetos de la
base de datos:

package com.objectdb.tutorial;

@Entity
public class Point {
// Persistent Fields:
private int x;
private int y;

// Constructor:
Point (int x, int y) {
this.x = x;
this.y = y;
}

// Accessor Methods:
public int getX() { return this.x; }
public int getY() { return this.y; }

// String Representation:
@Override
public String toString() {
return String.format("(%d, %d)", this.x, this.y);
}
}

Como puede ver arriba, una clase de entidad es una clase Java ordinaria. La
única adición única de JPA es la anotación @Entity , que marca la clase como
una clase de entidad.

Un intento de persistir Pointobjetos en la base de datos sin marcar


la Pointclase como clase de entidad provocará
una PersistenceException . Esto es similar a la excepción
NotSerializableException que lanza Java (no relacionada con JPA), en un
intento de serializar objetos no serializables (excepto que todas las excepciones
JPA están desactivadas cuando las excepciones de Java IO están marcadas).

Campos persistentes en clases de entidad


El almacenamiento de un objeto de entidad en la base de datos no almacena
métodos y códigos. Solo se almacena el estado persistente del objeto de
entidad, como se refleja en sus campos persistentes . Por defecto, cualquier
campo que no esté declarado como static o transientsea un campo
persistente. En nuestro ejemplo, los campos persistentes de la Pointclase de
entidad son xy y(representan la posición del punto en el plano). Son los valores
de estos campos los que se almacenan en la base de datos cuando persiste un
objeto de entidad.

El Capítulo 2 proporciona información adicional sobre cómo definir clases de


entidad, incluyendo qué tipos persistentes se pueden usar para campos
persistentes, cómo definir y usar una clave principal y qué es un campo de
versión y cómo usarlo.

Si ya está familiarizado con JPA, puede haber notado que la Pointclase de


entidad no tiene definido ningún campo de clave principal ( @Id ). Como una
base de datos de objetos, ObjectDB admite identificadores de objetos
implícitos, por lo que no se requiere una clave primaria explícitamente
definida. Por otro lado, ObjectDB también admite claves primarias explícitas
de JPA , incluidas las claves primarias compuestas y la generación de valor
secuencial automática . Esta es una característica muy poderosa de ObjectDB
que está ausente de otras bases de datos orientadas a objetos.

Obteniendo una conexión de


base de datos JPA
En JPA, una conexión de base de datos está representada por
la interfaz EntityManager . Por lo tanto, para manipular una base de datos
ObjectDB, necesitamos una instancia de EntityManager. Las operaciones que
modifican el contenido de la base de datos también requieren
una instancia.EntityTransaction

Obteniendo una EntityManagerFactory


La obtención de una instancia consta de dos pasos. Primero necesitamos
obtener una instancia de EntityManagerFactory que represente la base de
datos relevante y luego podemos usar esa instancia de fábrica para obtener
una instancia.EntityManagerEntityManager

JPA requiere la definición de una unidad de persistencia en un archivo XML para


poder generar un EntityManagerFactory. Pero al usar ObjectDB puede definir
una unidad de persistencia estándar en un archivo XML o simplemente puede
proporcionar la ruta de archivo de la base de datos ObjectDB:

EntityManagerFactory emf =
Persistencia . createEntityManagerFactory ( "$ objectdb / db /
points.odb" ) ;

El método estático createEntityManagerFactory espera un nombre de unidad


de persistencia como argumento, pero cuando usa ObjectDB, también se
acepta cualquier ruta válida de archivo de base de datos (absoluta o
relativa). Cualquier cadena que termine con .odb o .objectdb es considerada
por ObjectDB como una url de base de datos en lugar de un nombre de unidad
de persistencia.
El $objectdbprefijo especial representa el directorio de inicio de ObjectDB (por
defecto, el directorio en el que está instalado ObjectDB). Si todavía no existe
un archivo de base de datos en la ruta dada, ObjectDB intentará crear uno.

El EntityManagerFactory también se utiliza para cerrar la base de datos una


vez que termine de utilizarlo:

emf. close ( ) ;

Obteniendo un EntityManager
Una vez que tenemos una EntityManagerFactory , podemos obtener
una instancia fácilmente :EntityManager

EntityManager em = emf. createEntityManager ( ) ;

La EntityManagerinstancia representa una conexión a la base de


datos. Cuando se usa JPA, cada operación en una base de datos está asociada
con un EntityManager. Además, en una aplicación de subprocesos múltiples,
cada subproceso generalmente tiene su propia EntityManagerinstancia,
mientras que al mismo tiempo comparte una única
aplicación EntityManagerFactory.

Cuando la conexión a la base de datos ya no es necesaria, EntityManager se


puede cerrar:

em. close ( ) ;

Cerrar un EntityManager no cierra la base de datos en sí (ese es el trabajo de


fábrica como se explicó anteriormente). Una vez que se cierra
el objeto EntityManager , no se puede reutilizar. Sin embargo,
la instancia propietaria de EntityManagerFactory puede conservar
los recursos del EntityManager (como un puntero de archivo de base de
datos o un socket en un servidor remoto) en un grupo de conexiones y usarlos
para acelerar la futura construcción de EntityManager .

Usando una EntityTransaction


Las operaciones que modifican el contenido de la base de datos, como
almacenar, actualizar y eliminar, solo deben realizarse dentro de una
transacción activa.

Dado un em, es muy fácil comenzar una transacción:EntityManager

em. GetTransaction ( ) . begin ( ) ;

Existe una relación uno a uno entre una instancia de EntityManager y


sus instancias asociadas de EntityTransaction que devuelve
el método getTransaction .

Cuando una transacción está activa, puede invocar métodos


de EntityManager que modifican el contenido de la base de datos,
como persistir y eliminar . Las actualizaciones de la base de datos se
recopilan y gestionan en la memoria y se aplican a la base de datos cuando se
confirma la transacción:

em. GetTransaction ( ) . commit ( ) ;

La siguiente sección explica con más detalle cómo usar EntityManager y las
transacciones para las operaciones de la base de datos CRUD.

Ejecutando consultas JPA


La interfaz Query define dos métodos para ejecutar consultas SELECT:
 Query.getSingleResult : para usar cuandose espera exactamente
un objeto de resultado.
 Query.getResultList : para uso general en cualquier otro caso.

Del mismo modo, la interfaz TypedQuery define los siguientes métodos:


 TypedQuery.getSingleResult : para usar cuandose espera exactamente
un objeto de resultado.
 TypedQuery.getResultList : para uso general en cualquier otro caso.

Además, la interfaz Query define un método para ejecutar DELETE y UPDATE


consultas:
 Query.executeUpdate : solo para ejecutar DELETE y UPDATE consultas.
Esta página cubre los siguientes temas:

 Ejecución ordinaria de consultas (con getResultList)


 Ejecución de consultas de un solo resultado (con getSingleResult)
 ELIMINAR y ACTUALIZAR la ejecución de consultas (con executeUpdate)

Ejecución ordinaria de consultas (con getResultList)


La siguiente consulta recupera todos los objetos Country en la base de
datos. Dado que se esperan objetos de resultados múltiples, la consulta se
debe ejecutar utilizando el método getResultList :

TypedQuery <Country> query =


em. createQuery ( "SELECT c FROM Country c" , Country. class
) ;
Lista <País> resultados = consulta. getResultList ( ) ;

Tanto Query como TypedQuery definen un método getResultList , pero la


versión de Querydevuelve una lista de resultados de un tipo crudo (no
genérico) en lugar de un tipo parametrizado (genérico):

Query query = em. createQuery ( "SELECT c FROM Country c" ) ;


Lista de resultados = consulta. getResultList ( ) ;
Un intento de convertir los resultados anteriores a un tipo parametrizado
( Lista <País> ) provocará una advertencia de compilación. Sin
embargo, si se utiliza la nueva interfaz TypedQuery , no es necesario realizar
una conversión y se evita la advertencia.

La colección de resultados de consulta funciona como cualquier otra colección


Java común. Una colección de resultados de un tipo parametrizado se puede
iterar fácilmente utilizando un bucle for mejorado:

para ( País c : resultados ) {


Sistema. a cabo . println ( c. getName ( ) ) ;
}

Tenga en cuenta que para simplemente imprimir los nombres de los países, una
consulta que utiliza proyección y recupera nombres de países directamente en
lugar de instancias de país completamente construidas sería más eficiente.

Ejecución de consultas de un solo resultado (con


getSingleResult)
El método getResultList (que se discutió anteriormente) también se puede
usar para ejecutar consultas que devuelven un solo objeto de resultado. En
este caso, el objeto resultante debe extraerse de la colección de resultados
después de la ejecución de la consulta (por ejemplo, mediante results.get
(0) ). Para eliminar esta operación de rutina, JPA proporciona un método
adicional, getSingleResult , como método más conveniente cuando se espera
exactamente un objeto de resultado.

La siguiente consulta agregada siempre devuelve un único objeto de resultado,


que es un objeto largo que refleja el número de objetos de país en la base de
datos:

TypedQuery <Long> query = em. createQuery (


"SELECT COUNT (c) FROM Country c" , Long. class ) ;
largo countryCount = consulta. getSingleResult ( ) ;

Tenga en cuenta que cuando una consulta devuelve un solo objeto, puede ser
tentador preferir Query sobre TypedQuery incluso cuando se conoce el tipo de
resultado porque el envío de un solo objeto es fácil y el código es simple:

Query query = em. createQuery ( "SELECT COUNT (c) FROM Country c"
) ;
largo countryCount = Consulta ( larga ) . getSingleResult ( ) ;

Una consulta COUNT agregada siempre devuelve un resultado, por


definición. En otros casos, nuestra expectativa de un resultado de objeto único
puede fallar, dependiendo del contenido de la base de datos. Por ejemplo, se
espera que la siguiente consulta devuelva un único objeto Country:

Query query = em. createQuery (


"SELECCIONE c FROM País c WHERE c.name = 'Canadá'" ) ;
País c = consulta ( país ) . getSingleResult ( ) ;
Sin embargo, la exactitud de esta suposición depende del contenido de la base
de datos. Si la base de datos contiene múltiples objetos de País con el nombre
'Canadá' (por ejemplo, debido a un error), se
lanza una NonUniqueResultException . Por otro lado, si no hay ningún
resultado, se genera una excepción NoResultException . Por lo tanto, el uso
de getSingleResult requiere cierta precaución y, si existe la posibilidad de que
se lanzan estas excepciones, deben ser capturadas y manejadas.

ELIMINAR y ACTUALIZAR la ejecución de consultas


(con executeUpdate)
Las consultas DELETE y UPDATE se ejecutan utilizando
el método executeUpdate .

Por ejemplo, la siguiente consulta elimina todas las instancias del país :

int count = em. createQuery ( "DELETE FROM Country" ) .


executeUpdate ( ) ;

y la siguiente consulta restablece el campo del área en todas las instancias del
país a cero:

int count = em. createQuery ( "UPDATE Country SET area = 0" ) .


executeUpdate ( ) ;

Se lanza una TransactionRequiredException si ninguna transacción está


activa.

En caso de éxito, el método executeUpdate devuelve la cantidad de objetos que


la consulta ha actualizado o eliminado.

La sección Estructura de la consulta explica


las solicitudes DELETE y UPDATE con más detalle.

Parámetros de consulta en JPA


Los parámetros de consulta permiten la definición de consultas
reutilizables. Dichas consultas se pueden ejecutar con diferentes valores de
parámetros para recuperar resultados diferentes. Ejecutar la misma consulta
varias veces con diferentes valores de parámetros (argumentos) es más
eficiente que usar una nueva cadena de consulta para cada ejecución de
consulta, ya que elimina la necesidad de compilaciones de consulta repetidas.
Esta página cubre los siguientes temas:

 Parámetros con nombre (: nombre)


 Parámetros ordinales (índice?)
 Criterios Query Parameters
 Parámetros vs. Literales
 Métodos de parámetros API
Parámetros con nombre (: nombre)
El siguiente método recupera un objeto Country de la base de datos por su
nombre:

public País getCountryByName ( EntityManager em, String name ) {


TypedQuery <Country> query = em. createQuery (
"SELECT c FROM Country c WHERE c.name =: name" , Country.
class ) ;
consulta de devolución setParameter ( "nombre" , nombre ) .
getSingleResult ( ) ;
}

La cláusula WHERE reduce los resultados de la consulta a


los objetos Country cuyo valor de campo de nombre es igual a : name , que es
un parámetro que sirve como marcador de posición para un valor real. Antes de
que se pueda ejecutar la consulta, se debe establecer un valor de parámetro
utilizando el método setParameter . El método setParameter admite el
encadenamiento de métodos (al devolver la misma instancia TypedQuery en la
que se invocó), por lo que la invocación de getSingleResult se puede
encadenar a la misma expresión.

Los parámetros con nombre se pueden identificar fácilmente en una cadena de


consulta por su forma especial, que es un signo de dos puntos (:) seguido de
un identificador JPQL válido que sirve como el nombre del parámetro. JPA no
proporciona una API para definir los parámetros explícitamente (excepto
cuando se usa la API de criterios), por lo que los parámetros de consulta se
definen implícitamente al aparecer en la cadena de consulta. El tipo de
parámetro se deduce del contexto. En el ejemplo anterior, una comparación
de : nombre a un campo cuyo tipo es Cadena indica que el tipo de : nombre
en sí mismo es Cadena .

Las consultas pueden incluir múltiples parámetros y cada parámetro puede


tener una o más ocurrencias en la cadena de consulta. Una consulta solo puede
ejecutarse después de establecer valores para todos sus parámetros (en
cualquier orden en qué orden).

Parámetros ordinales (índice?)


Además del parámetro con nombre, cuya forma es : name , JPQL admite
también el parámetro ordinal, cuya forma es ? Index . El siguiente método es
equivalente al método anterior, excepto que un parámetro ordinal reemplaza el
parámetro nombrado:

public País getCountryByName ( EntityManager em, String name ) {


TypedQuery <Country> query = em. createQuery (
"SELECT c FROM Country c WHERE c.name =? 1" , Country.
class ) ;
consulta de devolución setParameter ( 1 , nombre ) .
getSingleResult ( ) ;
}
La forma de los parámetros ordinales es un signo de interrogación (?) Seguido
de un número int positivo. Además de la diferencia de notación, los parámetros
nombrados y los parámetros ordinales son idénticos.

Los parámetros con nombre pueden proporcionar un valor agregado a la


claridad de la cadena de consulta (suponiendo que se seleccionen nombres
significativos). Por lo tanto, se prefieren sobre los parámetros ordinales.

Criterios Query Parameters


En una consulta JPA que se genera utilizando la API de criterios JPA , los
parámetros (como otros elementos de consulta) se representan mediante
objetos (de tipo ParameterExpression o su parámetro superinterfase ) en
lugar de mediante nombres o números.

Consulte la sección Parámetros en Consultas de criterios para más detalles.

Parámetros vs. Literales


Lo que sigue es una tercera versión del mismo método. Esta vez sin
parámetros:

public País getCountryByName ( EntityManager em, String name ) {


TypedQuery <Country> query = em. createQuery (
"SELECCIONAR c FROM País c DONDE c.nombre = '" + nombre +
"'" ,
País. clase ) ;
consulta de devolución getSingleResult ( ) ;
}

En lugar de usar un parámetro para el nombre consultado, el nuevo método


incorpora el nombre como un literal de Cadena . Hay algunos inconvenientes al
uso de literales en lugar de parámetros en las consultas.

Primero, la consulta no es reutilizable. Diferentes valores literales conducen a


diferentes cadenas de consulta y cada cadena de consulta requiere su propia
compilación de consulta, que es muy ineficiente. Por otro lado, al usar
parámetros, incluso si se construye una nueva instancia de TypedQuery en cada
ejecución de consulta, ObjectDB puede identificar consultas repetitivas con la
misma cadena de consulta y usar un programa de consulta compilado en
caché , si está disponible.

En segundo lugar, incrustar cadenas en las consultas no es seguro y puede


exponer la aplicación a los ataques de inyección de JPQL. Supongamos que el
parámetro de nombre se recibe como una entrada del usuario y luego se
incrusta en la cadena de consulta tal como está. En lugar de un nombre de país
simple, un usuario malintencionado puede proporcionar expresiones JPQL que
cambian la consulta y pueden ayudar a piratear el sistema.

Además, los parámetros son más flexibles y admiten elementos que no están
disponibles como literales, como los objetos de entidad.
Métodos de parámetros API
Más de la mitad de los métodos en Query y TypedQuery se ocupan del manejo
de parámetros. La interfaz Query define 18 de esos métodos, 9 de los cuales
se anulan en TypedQuery . Esa gran cantidad de métodos no es típica de JPA,
que generalmente sobresale en su API delgada y simple.

Hay 9 métodos para establecer parámetros en una consulta, lo cual es esencial


cuando se usan parámetros de consulta. Además, hay 9 métodos para extraer
valores de parámetros de una consulta. Estos métodos de obtención, que son
nuevos en JPA 2, se espera que sean mucho menos utilizados que los métodos
establecidos.

Dos métodos de conjunto se muestran arriba: uno para establecer un


parámetro nombrado y otro para establecer un parámetro ordinal. Un tercer
método está designado para establecer un parámetro en una consulta API de
Criteria. La razón para tener nueve métodos establecidos en lugar de solo tres
es que JPA también proporciona tres métodos separados para configurar
los parámetros de Fecha , así como tres métodos separados para configurar
los parámetros del Calendario .

Los valores de los parámetros Fecha y Calendario requieren métodos


especiales para especificar lo que representan, como una fecha pura, una hora
pura o una combinación de fecha y hora, como se explica en detalle en
la sección Tipos de fecha y hora (Temporal) .

Por ejemplo, la siguiente invocación pasa un objeto Date como una fecha pura
(sin tiempo):

consulta. setParameter ( "date" , new java. util . Date ( ) ,


TemporalType . DATE ) ;

Desde TemporalType . La fecha representa una fecha pura, la parte de


tiempo de la instancia recién construida de java.util.Date se descarta. Esto
es muy útil en comparación con una fecha específica, cuando el tiempo debe
ser ignorado.

Los métodos get admiten diferentes formas de extraer parámetros y sus


valores de una consulta, incluidos por nombre (para parámetro con nombre),
por posición (para parámetros ordinales) por objeto Parameter (para consultas
API Criteria), cada uno con o sin un tipo esperado. También hay un método
para extraer todos los parámetros como un conjunto ( getParameters ) y un
método para verificar si un parámetro especificado tiene un valor
( isBound ). Estos métodos no son necesarios para ejecutar consultas y se
espera que se utilicen con menos frecuencia.

Potrebbero piacerti anche