Mapeando relaciones y colecciones en hibernate Diciembre 2008
JosĂŠ A. GarcĂa jagarcia@siteframe.tk
El Jueves vimos..
• Como configurar el entorno de hibernate • Como realizar el mapeo de tipos básicos • Como configurar un generador básico • Como persistir una clase java simple usando hibernate
2
Más sobre valuetypes
• El desafío es mapear el sistema de tipos de Java a un sistema del tipo SQL/BD. El puente entre ambos sistemas es provisto por Hibernate. • Para las entidades existen clases (<class>) y subclases (<subclass>). Para los "value types" usamos <property>, normalmente con un atributo type. • El valor de este atributo es uno de los tipos para mapeo de Hibernate. Hibernate trae varios mapeos (para los tipos estándar de la JDK) ya incluidos e incluso permite crear tipos nuevos. 3
Mรกs sobre valuetypes
4
Mรกs sobre valuetypes
5
Mapeo de grandes bloques de datos Cuando queremos almacenar una gran cantidad de información generalmente recurricumos a tipos especiales en la BBDD. Por ejemplo Mysql y Sqlserver proveen el tipo TEXT para almacenar textos grandes (de varias páginas). Oracle Express incorpora los tipos CLOB y BLOB para almacenar textos y binarios de hasta 4GB. Hibernate soporta también esos tipos especiales:
6
Generadores de identificadores En el ejemplo que hicimos el último dia hemos visto los generadores “native” en HB3 existen algunos tipos más de generadores:
Generadores de identificadores En el ejemplo que hicimos el último dia hemos visto los generadores “native” en HB3 existen algunos tipos más de generadores:
Generadores de identificadores En Oracle XE una secuencia normalmente lleva asociado un trigger que realiza el reemplazo luego en este caso la mejor opci贸n ser铆a hacer que el trigger se ejecutara solo en caso de conflictos y hacer que hibernate tome directamente la secuencia.
Ejercicios básicos Utilicemos la tabla Libros que creamos en la parte de Oracle o genera una nueva con su correspondiente pareja secuencia-trigger de reemplazo. Mapeemos la tabla con el asistente de netBeans. • La tabla libros tenia un identificador único id_libro que es PK en la tabla. Por defecto NB nos asocia un generador de tipo “assigned” . ¿Qué ocurre si intentamos insertar un valor de ID que ya existe? • Cambiar el tipo de generador a “sequence”. Intentar insertar un valor, que ocurre? • Haz los cambios que consideres oportunos para evitar el error anterior. Haz varias inserciones. Que problema observas ahora? • Plantea alguna solución para el problema anterior (pequeña modif. en trigger).
Ejercicios bรกsicos create or replace trigger "BI_LIBROS" before insert on "LIBROS" for each row declare curr "LIBROS".ID%TYPE; exis NUMBER; begin select count(ID) into exis from "LIBROS" where ID=:NEW.ID; select "LIBROS_SEQ".currval into curr from dual; if ((:NEW.ID>curr+1) OR (exis>0)) THEN select "LIBROS_SEQ".nextval into :NEW.ID from dual; end if; end;
org.hibernate.id.enhanced.SequenceStyleGenerator Las incompatibilidades entre diferentes BBDD a la hora de generar identificadores han llevado a que Hibernate incorpore sus propios generadores externos. Este es el caso del SequenceStyleGenerator. Lo que hace el SSG es simular el comportamiento de una secuencia implementandolo a travĂŠs de tablas en las BBDD que no soportan sequencias.
org.hibernate.id.enhanced.SequenceStyleGenerator ...... <id name="tid" type="java.lang.Long"> <column name="TID" precision="16" scale="0" /> <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator"> <param name="sequence_name">LIBROS_SEQ</param> <param name="initial_value">1</param> <param name="increment_size">10</param> </generator> ....... </id>
Volvamos al ejemplo del Jueves public class Message { private Long id; private String text; private Message nextMessage; private Message() {} public Message(String text) { this.text = text; } public Long getId() { return id; } private void setId(Long id) { this.id = id; } public String getText() { return text; }
public void setText(String text) { this.text = text; } public Message getNextMessage() { return nextMessage; } public void setNextMessage(Message nextMessage) { this.nextMessage = nextMessage; } }
14
Vimos como persistir un mensaje â&#x20AC;˘ La clase mensaje puede ser usada como cualquier otra clase Message message = new Message("Hello World"); System.out.println( message.getText() );
â&#x20AC;˘ Usamos hibernate para persistir los datos a la BBDD Session session = getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); Message message = new Message("Hello World"); session.save(message); tx.commit(); session.close();
15
En la base de datos … • El Código de traduce a insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID) values (1, 'Hello World', null)
16
Para recrear el objeto haríamos • newSession.load ( clase del objeto, idObj ) Session newSession = getSessionFactory().openSession(); Transaction newTransaction = newSession.beginTransaction(); Event loadedEvent = (Event)session.load(Event.class, e.getId()); System.out.println(loadedEvent.getName()); newTransaction.commit(); newSession.close();
17
Para ejecutar una query • Session.CreateQuery (“sentencia HSQL”) Session newSession = getSessionFactory().openSession(); Transaction newTransaction = newSession.beginTransaction(); List messages = newSession.createQuery("from Message as m order by m.text asc"); System.out.println( messages.size() + " message(s) found:" ); for ( Iterator iter = messages.iterator();iter.hasNext(); ) { Message message = (Message) iter.next(); System.out.println( message.getText() ); } newTransaction.commit(); newSession.close();
18
En la Base de Datos .. • se traduciría a: select m.MESSAGE_ID, m.MESSAGE_TEXT,m.NEXT_MESSAGE_ID from MESSAGES m order by m.MESSAGE_TEXT asc
NOTA: ES IMPORTANTE NOTAR LA DIFERENCIA ENTRE EL HSQL Y EL CODIGO SQL FINAL.
19
Ejercicio 2 (basico) Crea la tabla mensaje y su correspondiente secuencia. Inserta algunas filas y obten el mapeo de hibernate. Escribe el siguiente código: Session session = getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); // La fila 1 debe existir Message message = (Message) session.load( Message.class, 1 ); message.setText(“Hola mundo!”); Message nextMessage = new Message(“queda mucho por hacer”); message.setNextMessage( nextMessage ); tx.commit(); session.close(); 20
Ejercicio 2 (basico) 多Que acabamos de hacer? 多Cuantas sentencias SQL ha lanzado Hibernate? Ojea el fichero de mapeo, donde estan las relaciones for叩neas? Veremos con mas detalle el mapeo de asociaciones ma単ana. Modificar la tabla para que el texto se almacene en una columna clob de Oracle.
21
Recordemos las diferencias entre MR y MOO NO SE CONSIDERAN LAS CLASES NO PERSISTENTES
Recordemos las diferencias entre MR y MOO
NO SE CONSIDERAN LAS RELACIONES NO ESTRUCTURALES p.e: administrativo GENERA informes
Recordemos las diferencias entre MR y MOO
NO EXISTE LA HERENCIA: se originan entidades dĂŠbiles o especializaciones con discriminante
COPIA
SECRETARIO
GERENTE
COMERCIAL
Categoría
Nombre Código
Docente Código
Nombre
Trabajador
Departamento ES
Área
Docente
Administrativo
Administrativo Categoría Código
Nombre
Departamento
Área
Representación de la generalización en MR • Representación de la generalización Método 1: 1. Crear una tabla para el {} de entidades de nivel más alto (atributos claves: a1, a2, …, am ) 2. Crear una tabla para cada {} de entidades de nivel más bajo (Atributos: b1, b2, …, bn) • Con: {b1, b2, …, bn} {a1, a2, …, am} columnas Persona (nombre, calle ciudad) Empleado (nombre, sueldo) Cliente ( nombre, límite _crédito)
Representación de la generalización en MR • Representación de la generalización Método 2: (si es disjunta y completa) 1. Crea una tabla para cada entidad del {} de entidades de nivel más alto con: {discriminante} {a1, a2, …, am} columnas 2. se crean tablas secundarias para las entidades de nivel mas bajo incluyendo la clave de la tabla mas alta: {b1, b2, …, bn}: atributos de la entidad {a1, a2, …, am}: atributos clave de la entidad de nivel más alto Empleado (nombre, calle, ciudad, sueldo, idpersona) Cliente ( nombre, calle, ciudad, límite _crédito, idpersona) Persona (id, nombre, calle, ciudad, tipo)
Mapear relaciones de herencia en Hibernate
Hibernate define 3 estrategias para mapear herencia: - Basada en una Ăşnica tabla para toda la jerarquia de clases - Basada en una tabla por cada subclase concreta - Basada en una tabla por cada subclase y un discriminante
28
una única tabla para toda la jerarquia de clases • Supongamos que tenemos una interfaz Payment (pago), implementada por las clases CreditCardPayment, CashPayment, ChequePayment (pago por tarjeta de crédito, efectivo y cheque respectivamente). El mapeo de "una tabla por jerarquía" se vería así:
29
una Ăşnica tabla para toda la jerarquia de clases
30
Basada en una tabla por cada subclase concreta â&#x20AC;˘ En este caso la herencia se implementa mediante un cruce (<joined-subclass>):
31
Basada en una tabla por cada subclase concreta
32
Basada en una tabla por cada subclase concreta y un discriminante â&#x20AC;˘ En este caso usamos juntos <subclass> y <join>:
33
Basada en una tabla por cada subclase concreta y un discriminante
34
Ejercicio • Supongamos las clases: – Mensaje Leido
•Guardan además del texto del mensaje, el nombre del lector y la fecha de la transcripción. – Mensaje Multimedia
•Pueden guardar además del texto del mensaje un enlace a una imagen y una referencia a un fichero multimedia (musica,video..) Definir las relaciones de herencia y su adecuado mapeo en Hibernate
35