47
Ing. Ronald Cuello Meza

Persistencia De Objetos(Hibernate)

Embed Size (px)

Citation preview

Ing. Ronald Cuello Meza

Que es un ORM

Es una técnica de programación para convertir datos entre el sistema de tipos

utilizado en un lenguaje de programación orientado a objetos y el utilizado en

una base de datos relacional. En la práctica esto crea una base de datos

orientada a objetos virtual, por sobre la base de datos relacional. Esto posibilita

el uso de las características propias de la orientación a objetos (básicamente

herencia y polimorfismo). Hay paquetes comerciales y de uso libre disponibles

que desarrollan el mapeo relacional de objetos, aunque algunos programadores

prefieren crear sus propias herramientas ORM. (Fuente Wikipedia)

Ventajas de un ORM

No trabajar con filas de tablas (DataRows,RecordSet,ResultSet)

Trabajar con las clases diseñadas en su modelo del dominio

Permite elegir la base de datos relacional con la que queramos interactuar (SQLServer,PostGreSQL,MySQL,Oracle…)

Genera automáticamente el código SQL usando un mapeo objeto-relacional ,el cual se especifica en un documento XML o anotaciones

Permite crear,modificar,recuperar y eliminar objetos persistentes recuperarlos,ademas nos permite navegar por las asociaciones entre objetos y luego actualizarlos al finalizar una transacción.

Frameworks ORMSegún el lenguaje a utilizar existen varios frameworks de código abierto que

resuelven el mapeo objeto-relacional comerciales y libres que realizan mapeo

objeto-relacional. Por citar algunos:

•NeXT

• Enterprise Objects Framework

•OpenStep

•WebObjects

• Java Data Objects (JDO)

• Enterprise Java Beans

•Hibernate

•NHibernate

•iPersist

•Linq

•EntityFramework

•CastleActiveRecord

•Oracle TopLink

ConclusiónUtilizar un framework de ORM simplifica enormemente la programación de

lógica de persistencia.

Lógica de negocios basada en un modelo de dominio completamente orientado

a objetos.

Ahorro de código y más simple y fácil de mantener.

ORM nos proporciona grandes beneficios :

•Independencia de la base de datos

•Bajo acoplamiento entre negocios y persistencia

•Desarrollo rápido

HibernateUsar JDBC es complejo y muy dependiente de la estructura de los datos. Sería

más natural y mucho más sencillo trabajar directamente con objetos, pero es

imposible con las BBDD relacionales, y las BBDD orientadas a objeto falta

mucho por implementar.

La mejor opción entonces es utilizar un motor de persistencia, que es el

componente software encargado de traducir entre objetos y registros. Un motor

de persistencia de código abierto es Hibernate, que nos permitirá hacer cosas

como poder guardar un objeto en la base de datos simplemente con

session.save(miObjeto) o borrarlo con session.delete(miObjeto).

Que es Hibernate

Es una herramienta de Mapeo objeto-relacional para la plataforma Java que

facilita el mapeo de atributos entre una base de datos relacional tradicional y el

modelo de objetos de una aplicación, mediante archivos declarativos (XML) que

permiten establecer estas relaciones.

Usa el mecanismo de reflexión de Java, que permite a un objeto en ejecución

examinarse y manipularse a sí mismo.

Características

•No es Intrusivo ( estilo POJO plain old java objects)

•Buena documentación y comunidad amplia y activa

•Manejo de transacciones,Caché,asociaciones,polimorfismo,herencia,persistencia

transitiva, estrategias de fetching

•Potente lenguaje de consulta (HQL) ,subqueries,outer

joins,ordering,paginacion,etc

•Fácil testeo

•Cada clase persistente necesita un fichero XML de mapeo del que obtiene toda

la información para realizar las operaciones CRUD

•No es un Standard (Es un Framework)

Hibernate Vs JDBCSin Hibernate, para añadir un registro a la tabla Empleados tendríamos que

escribir algo similar a esto:

Con Hibernate, escribiríamos algo similar a esto:

[...]

Class.forName(“org.hsqldb.jdbcDriver”);

String url = “jdbc:hsqldb:./Databases/MiEmpresa”;

Connection connection = DriverManager.getConnection(url, “sa”, “”);

String ins = “INSERT INTO EMPLEADOS VALUES(NULL, „Ronald„,‟Cuello‟)”;

Statement stmt = null;

stmt = connection.createStatement();

stmt.executeUpdate(ins);

[...]

[...]

Configuration conf = new Configuration();

conf.addClass(Empleado.class);

SessionFactory sessionFactory = conf.buildSessionFactory();

Session session = sessionFactory.openSession();

Empleado e = new Empleado();

e.setNombre(“Ronald”); e.setApellido(“Cuello”);

session.save(e);

session.flush();

session.close();

[...]

Hibernate Vs JDBC

¿Cuál es la gran diferencia entre el código tradicional JDBC y el código que

escribimos con Hibernate? “Ha desparecido el SQL”. Lo único que digo es que

quiero “guardar” (save) un objeto.

Dialectos de HibernateHibernate soporta, actualmente, los siguientes dialectos:

Base de datos Dialecto

DB2 net.sf.hibernate.dialect.DB2Dialect

DB2 AS/400 net.sf.hibernate.dialect.DB2400Dialect

DB2 OS390 net.sf.hibernate.dialect.DB2390Dialect

PostgreSQL net.sf.hibernate.dialect.PostgreSQLDialect

MySQL net.sf.hibernate.dialect.MySQLDialect

Oracle (cualquier versión) net.sf.hibernate.dialect.OracleDialect

Oracle 9/10g net.sf.hibernate.dialect.Oracle9Dialect

Sybase net.sf.hibernate.dialect.SybaseDialect

Sybase Anywhere net.sf.hibernate.dialect.SybaseAnywhereDialect

Microsoft SQL Server net.sf.hibernate.dialect.SQLServerDialect

SAP DB net.sf.hibernate.dialect.SAPDBDialect

Y otros mas . . .

EjemploDiagrama de entidad relación.

EjemploDiagrama de clases.

Que se necesita

1. Una serie de JavaBeans que son las clases a persistir

2. Un archivo XML por cada una de estas clases (MiClase.hbm.xml) que indica el

mapping entre objetos y relaciones

3. Crear/Configurar el archivo del framework hibernate.cfg.xml

Configurar JavaBean

Este sería el aspecto del JavaBean que representa un Empleado en una

supuesta aplicación:

package mx.model;

public class Empleado implements Serializable{

private Long id;

private String nombre;

private String apellido;

// Setters Getters

….

}

Estructura del fichero XML

Esquema general de un fichero XML de mapeo es algo como esto:

<Encabezado XML>

<Declaración de la DTD>

<class - Definición de la clase persistente>

<id - Identificador>

<generator - Clase de apoyo a la generación automática de OID's>

<property – propiedades de la clase>

<!--***************Asociaciones *************** -->

<!--Posibles relaciones con otras entidades persistentes-->

<one-to-many >

<many-to-many>

<many-to-one>

12

34

5

6

Estructura del fichero XML

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD

3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="mx.model.Empleado" table="empleados">

<id name="id" column="id">

<generator class="native"/>

</id>

<property name="nombre" column="nombre" />

<property name="apellido" column="apellido" />

</class>

</hibernate-mapping>

Estructura del fichero XML

Declaración de la DTD.

El documento DTD que usaremos en nuestros ficheros XML se encuentra en cada distribución de

Hibernate en el propio .jar o en el directorio src.

Elemento Raíz <hibernate-mapping>. Dentro de él se declaran las clases de nuestros objetos

persistentes. Aunque es posible declarar más de un elemento <class> en un mismo fichero XML, no

debería hacerse ya que aporta una mayor claridad a nuestra aplicación realizar un documento XML por

clase de objeto persistente.

1

2

<class>.

Este es el tag donde declaramos nuestra clase persistente. Una clase persistente equivale a una tabla

en la base de datos, y un registro o línea de esta tabla es un objeto persistente de esta clase. Entre sus

posibles atributos destacaremos :

•name : Nombre completo de la clase o interface persistente. Deberemos incluir el package dentro del

nombre.

•table : Nombre de la tabla en la BBDD referenciada. En esta tabla se realizaraá las operaciones de

transacciones de datos. Se guardarán, modificarán o borrarán registros según la lógica de negocio de la

aplicación.

•discriminator-value : Permite diferenciar dos sub-clases. Utilizado para el polimorfismo.

•proxy : Nombre de la clase Proxy cuando esta sea requerida.

Estructura del fichero XML3

<id>

Permite definir el identificador del objeto. Se corresponderá con la clave principal de la tabla en la

BBDD. Es interesante definir en este momento lo que será para nuestra aplicación un OID( Identificador

de Objeto ). Tenemos que asignar identificadores únicos a nuestros objetos persistentes, en un primer

diseño podríamos estar tentados a asumir un dato con significado dentro de la capa de negocios del

propio objeto fuese el identificador, pero esta no seria una buena elección.

4<generator>

clase que utilizaremos para generar los oid's

5 <property>

Declara una propiedad persistente de la clase , que se corresponde con una columna.

•name: Nombre lógico de la propiedad.

•column: Columna en la tabla.

•type: Indica el tipo de los datos almacenados.

6Tipos de relaciones.(Componentes y Colecciones)

En todo diseño relacional los objetos se referencian unos a otros a través de relaciones, las típicas son :

uno a uno 1-1, uno a muchos 1-n, muchos a muchos n-m, muchos a uno n-1. De los cuatro tipos, dos

de ellas n-m y 1-n son colecciones de objetos las cuales tendrán su propio y extenso apartado, mientras

que a las relaciones 1-1 y n-1 son en realidad componentes de un objeto persistente cuyo tipo es otro

objeto persistente.

Estructura del archivo de

configuración

Esquema general de un fichero XML de configuración es algo como esto:

<Encabezado XML>

<Declaración de la DTD>

< hibernate-configuration - Definición de la configuración>

<session-factory – parámetros de configuración>

<property – especificación de los parámetros de configuración>

<mapping - especificación de los archivos de mapeo>

Estructura del archivo de

configuración

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<property name="hibernate.dialect">

org.hibernate.dialect.MySQLDialect

</property>

<property name="hibernate.connection.driver_class">

com.mysql.jdbc.Driver

</property>

<property name="hibernate.connection.url">

jdbc:mysql://localhost:3306/base_de_datos

</property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password">123456</property>

<mapping resource="mx/model/persistance/Empleado.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Configuración de la API En cualquier aplicación que use Hibernate aparecen cuatro objetos básicos:

Configuration cfg = new Configuration();

cfg.configure(RUTA_ARCHIVO_CONF);

//agrega do clases dinamicamente

Cfg.addClass(MiClase.Class);

Configuration: es el objeto que contiene la información necesaria para conectarse a

la base de datos. Es el encargado de leerse el archivo. Hibernate.properties o

Hibernate.cfg.xml

También es el encargado de procesar la información correspondiente a los

aparejamientos. Es el encargado de leerse y verificar los archivos de emparejamiento

nombreDeClasse.hbm.xml.

Configuración de la API

SessionFactory: es una fábrica de Sessions. Un objeto Configuration es capaz de

crear una SessionFactory ya que tiene toda la información necesaria.

Normalmente, una aplicación sólo tiene una SessionFactory.

A partir del hibernate.cfg.xml podemos crear un SessionFactory, que es el objeto

mediante el cual abrimos nuevas sesiones de Hibernate.

SessionFactory sessionFactory = cfg.buildSessionFactory();

Configuración de la API

Session: La principal interfaz entre la aplicación Java e Hibernate. Es la que mantiene las conversaciones entre la aplicación y la base de datos. Permite añadir, modificar y borrar objetos en la base de datos.

Session session= sessionFactory.openSession();

Configuración de la API

Transaction: Como su nombre indica, se encarga de la transaccionalidad. Permite definir unidades de trabajo.

Transaction tx = session.beginTransaction();

[...]session.save(unaCategoria);

tx.commit();

[...]

tx.rollback();

Esquema de Configuración

Configuration

SessionFactory Base de

Datos

Session Session

- Hibernate.properties

- Hibernate.cfg.xml

- MiClase.hbm.xml

- addClass

Save – Update – Delete – get -load

BeginTransacction

Commit

RollBack

Configuración de la APIPara insertar objetos en la BBDD usaremos el método save(Object objeto)

de Session, para insertar o actualizar si ya existe saveOrUpdate(Object

objeto), para borrar delete(Object objeto) y para cargar un objeto desde la

BBDD get(String clase, Tipo id) o load(String clase, Tipo id) que

devuelven el objeto de la clase indicada por el primer parámetro y con

identificador el segundo parámetro si es que existe en la BBDD.

Para hacer búsquedas en la base de datos podemos usar find(String

consulta) que devuelve una lista con los resultados, o iterate(String

consulta) que devuelve un iterador, o bien crear un objeto Query usando

createQuery(String consulta) y llamar más tarde al método find() del Query.

La consulta en todos los casos estará escrita en un lenguaje similar a SQL

propio de Hibernate llamado HQL (Hibernate Query Language)

Programando la APIpublic class HibernateUtil {

private static final SessionFactory sessionFactory;

static {

try {

// Create the SessionFactory from standard (hibernate.cfg.xml)

// config file.

String path="mx/model/persistance/hibernate.cfg.xml";

Configuration cf=new Configuration();

cf.addFile(path);

sessionFactory=cf.buildSessionFactory();

} catch (Throwable ex) {

// Log the exception.

System.err.println("Initial SessionFactory creation failed." + ex);

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory() {

return sessionFactory;

}

}

Creación de CRUDpublic static List findAll(){

Session session=HibernateUtil.getSessionFactory().openSession();

Query q=session.createQuery("From Empleado e");

return q.list();

}

public static Empleado findById(Long id){

Session s=HibernateUtil.getSessionFactory().openSession();

return (Empleado)s.get(Empleado.class, id);

}

public static void save(Empleado e){

Session s=HibernateUtil.getSessionFactory().openSession();

s.getTransaction().begin();

try{

s.persist(e);

s.getTransaction().commit();

}catch(Exception exc){

s.getTransaction().rollback();

exc.printStackTrace();}

ColeccionesLas colecciones son declaradas utilizando

<set>, <list>, <map>, <bag>, <array> y <primitive-array>. Los posibles

parámetros y sus valores son :

•name: El nombre lógico de la propiedad. Es útil poner un nombre que nos recuerde

el rol de la colección (ej: cargos, departamentos, etc.. )

•table: Nombre de la tabla de la colección.

•lazy ("true"|"false"): Permite el uso de inicialización "lazy". Este tipo de

inicialización hace que los objetos de la colección sean solicitados en demanda y no

se carguen todos a la vez. Esto es especialmente útil para optimizar búsquedas, etc...

•inverse: Señala esta colección como el fin de una asociación bidireccional. Utilizada

en relaciones many-to-many sobre todo.

•cascade: Permite las operaciones en cascada hacia las entidades hijas.

•sort: Especifica una colección con una ordenación natural o con una clase

comparadora dada.

•order-by: Columna\s de la tabla que definen el orden de iteración. Puede ser

ascendente o descendente.

Colecciones (Asociaciones)<one-to-one> ( 1 - 1)

Definición:Es nuestra primera relación simple. En esta relación ,una Clase tiene una referencia a una

instancia a otra Clase y

están relacionadas bajo una misma llave PK,aunque si no manejamos integridad referencial

podemos hacer que sea el índice que queramos , como puede ser una FK o cualquier otro valor

que tengan en común las dos tablas. Esta relación puede ser BIDIRECCIONAL

Escenario:

Tenemos 2 clases ,Empleado y Departamento relacionados de la

siguiente manera:

Empleado Departamento.getCoordinador()//retorna Departamento

Departamento Empleado.getDepartamento()//retorna Empleado

1

Colecciones (Asociaciones)1 <one-to-one> ( 1 - 1)

Si la clase B no posee la misma id del padre, se generará un error de

ejecución.

Mapping Departamento:<class name=“Departamento" table=“departamentos">

<one-to-one name=“coordinador" class="mx.model.Empleado" />

</class>

Mapping Empleado:<class name=“Empleado" table=“empleados">

<one-to-one name=“departamento" class="mx.model.Departamento" />

</class>

Colecciones (Asociaciones)2

<many-to-one> ( 1 - 1)

Definición:

En esta relación se maneja de la siguiente

forma, una llave foránea en una tabla esta

referenciando la llave primaria de

otra tabla

Escenario:

Tenemos 2 clases ,Empleado y Departamento

relacionados de la siguiente manera:

Empleado Departamento.getCoordinador()

//retorna Empleado

Hibernate – Mapping:<class name=“Departamento" table=“departamentos">

<many-to-one name="coordinador"

column="fk_coordinador" class="mx.model.Empleado"/>

</class>

Colecciones (Asociaciones)<one-to-many> ( 1 - *)

Definición:

La Clase A debe tener una colección que

referencie a la Clase B

Escenario:

Tenemos 2 clases ,Empleado y

Departamento relacionados de la siguiente

manera:

Set Empleado.getDepartamentos()

//retorna Colección de instancias de

Departamento

Hibernate – Mapping:<class name=“Empleado" table=“empleados">

<set name="departamentos" table="departamentos">

<key column="fk_coordinador"/>

<one-to-many class="mx.model.Departamento"/>

</set>

</class>

3

Colecciones (Asociaciones)4

<many-to-many> ( n - m)

Definición:

Relación de muchos a muchos donde la

clase A posee una colección que referencia

a B,como la relación oneToMany,pero B

puede tener múltiples A.

Escenario:

Tenemos 2 clases ,Empleado y Cargo

relacionados de la siguiente manera:

Set<Cargo>Empleado.getCargos()

//retorna colección de instancias de Cargo

Hibernate – Mapping:<class name=“Empleado" table=“empleados">

<set name="cargos" table="empleado_cargos">

<key column="fk_empleado"/>

<many-to-many class="mx.model.Cargo"

column="fk_cargo" />

</set>

</class>

Hibernate Query Language

(HQL)El HQL (Hibernate Query Language) es un lenguaje de consulta.

En el mundo relacional disponemos del SQL (Structured Query Language)

que nos permite obtener información haciendo preguntas basadas en las

tablas y sus columnas.

El equivalente en el mundo objetual es el HQL, que nos permite hacer

preguntas basadas en los objetos y sus propiedades.

Una vez más, Hibernate se encarga de unir los dos mundos. Traduce las

consultas que hacemos desde el mundo objetual en HQL al lenguaje de

consulta del mundo relacional, el SQL, y transforma los resultados

obtenidos en el mundo relacional (filas y columnas) en aquello que tiene

sentido en el mundo objetual: objetos.

Hibernate Query Language

(HQL)El concepto de “traducción” es importante para entender qué hace

Hibernate y uno de los sentidos de HQL. Hemos visto más arriba la

equivalencia entre una consulta SQL y una en HQL. Así, la consulta

FROM Empleado e WHERE e.id = 1

se podría “traducir” a

SELECT ID, NOMBRE, APELLIDOS FROM empleados WHERE id = 1

y la consulta

FROM Empledo e WHERE e.nombre = ‘Ronald‘

se podría “traducir” a

SELECT ID, NOMBRE, APELLIDOS FROM Empleados e

WHERE e.NOMBRE = ‘Ronald’

Hibernate Query Language

(HQL)Clausulas :

FROM

Existen varias formas de simplificar una clase a la hora de realizar la consulta

hql> FROM mx.model.Empleado

hql> FROM Empleado

Dando como resultado una colección de objetos de tipo Empleado

SELECT

la clausula SELECT selecciona cual(es) objeto (s)y cual (es) propiedad(es) se retornara en el

resultado del query.

hql> SELECT v.marca FROM Vehiculo as v

hql> SELECT v.marca FROM Vehiculo v WHERE v.marca like „toy%‟

hql> SELECT user.persona.nombre FROM Usuario user

hql> SELECT user.username,p.nombre FROM Usuario user,Persona p

La anterior consulta nos retorna Object[ ]

WHERE

Ayuda a filtrar la lista de instancias retornadas.

hql> FROM Usuario user WHERE user.username = „rcuello‟

hql> FROM Usuario user WHERE user.persona.cedula=123456

hql> FROM Usuario user WHERE username.username IS NOT NULL

Hibernate Query Language

(HQL)ORDER BY

La lista retornada por una consulta puede ser ordenada por cualquier propiedad de un objeto.

hql> FROM Empleado emp ORDER BY emp.nombre asc

hql> FROM Usuario user ORDER BY user.persona.apellido desc

GROUP BY

Def SQL :Combina los registros con valores idénticos, en la lista de campos especificados, en un

único registro.

tienda ganancia fecha

Mcdonalds 1 200 000 05-Jan-1999

Kokoriko 950 000 07-Jan-1999

Mcdonalds 800 000 08-Jan-1999

American Brosted 720 000 08-Jan-1999

Tabla. Tiendas_info

Deseamos saber las ventas totales para cada negocio. Para hacerlo, ingresaríamos,

SQL > SELECT tienda, SUM( ganancia ) FROM Tiendas_info GROUP BY tienda

tienda SUM ( ganancia )

Mcdonalds 2 000 000

Kokoriko 950 000

Amrican Brosted 720 000

Dando como resultado

Hibernate Query Language

(HQL)GROUP BY

hql> Select t.tienda,SUM (t.ganancia) From TiendaInfo t GROUP BY t.tienda

El resultado de esta consulta es una Lista de Objects de tipo arreglo (Object[ ])

Ejemplo:

Query q=session.createQuery(“Select t.tienda,SUM (t.ganancia) From TiendaInfo t GROUP BY t.tienda “);

List lista =q.list();

For(Object obj : lista){

Object[ ] listaObjetos=(Object[ ])obj;

//donde la posicion 0 es igual a t.tienda

//la posicion 1 es igual a SUM ( t.ganancia )

}

Hibernate Query Language

(HQL)FUNCIONES ESCALARES

HQL nos permite usar funciones escalares en nuestras consultas. Y las

soportadas son las siguientes :

avg(...) count(*)

sum(...) count(...)

min(...) count(distinct ...)

max(...) count(all...)

Todas tienen las mismas funcionalidades del SQL

SELECT COUNT(*) FROM Empleado

En nuestro ejemplo, no tenemos más números que los id de las clases, así que no puedo poner ejemplos

demasiado interesantes ni prácticos de las funciones escalares, pero a guisa de ejemplo, podemos obtener la

media de los id de Empleado:

SELECT AVG(e.id) FROM Empleado e

También los podemos sumar:

SELECT SUM(e.id) FROM Empleado e

O obtener los valores máximo y mínimo:

SELECT MAX(e.id), MIN(e.id) FROM Empleado e

Hibernate Query Language

(HQL)

Las expresiones estan permitidas para la clausula WHERE ,y se incluyen

casi la gran mayoria de SQL:

Expresiones

•Operadores matemáticos +, -, *, /

•Operadores de comparacion binaria =, >=, <=, <>, !=, like

•Operaciones logicas and, or, not

•Concatenacion de cadenas ||

•Funciones escalares SQL upper() , lower()

•Parentesis ( ) indica agrupacion

•in, between, is null

•Parametros posicionales ?

•Parametros nombrados :parametro, :start_date, :x1

•Literales SQL'foo', 69, '1970-01-01 10:00:01.0'

•Valores de Enumeraciones y constantes Vehiculo.Color.RED

Ejercicios1

Ejercicios2

3

Ejercicios

4

5