Manual Hibernate V 2
Manual Hibernate V 2
HIBERNATE
NDICE 1 INTRODUCCIN .................................................................................................................... 3 1.1 1.2 Object-Relational Mapping.......................................................................................... 3 Hibernate y MVC en entornos web ............................................................................. 5
MI PRIMERA APLICACIN CON HIBERNATE ......................................................................... 7 2.1 2.2 Pasos para crear una aplicacin Hibernate ................................................................. 7 Desarrollo paso a paso................................................................................................. 8
FRAMEWORK HIBERNATE .................................................................................................. 38 3.1 3.2 3.3 3.4 Introduccin............................................................................................................... 38 Instalacin.................................................................................................................. 39 Configuracin............................................................................................................. 39 El interfaz Session ...................................................................................................... 58
ASOCIACIONES.................................................................................................................... 62 4.1 4.2 4.3 4.4 Relaciones en Bases de datos .................................................................................... 63 Many-to-one .............................................................................................................. 65 Many-to-many ........................................................................................................... 67 One-to-one................................................................................................................. 69
RECUPERACIN DE DATOS ................................................................................................. 71 5.1 5.2 5.3 HQL ............................................................................................................................ 73 Criteria ....................................................................................................................... 83 SQL ............................................................................................................................. 89
[2]
HIBERNATE
1. INTRODUCCIN
1.1.OBJECT-RELATIONAL MAPPING
Hibernate es una implementacin de mapeo Objeto-Relacional (ORM Object-Relational Mapping). El objetivo de un ORM es hacer corresponder el modelo de datos de la aplicacin con el modelo de clases de la misma, los cuales no son tan distintos como se pueda pensar. Para ello, Hibernate realiza el mapeo de elementos (tablas, columnas) entre una Base de datos relacional y objetos de la aplicacin en cuestin:
Modelo de Objetos
TABLA Persona Nombre Juan Ana Francisco Beln Lpez Hurtado Soria Jimnez Apellido Telfono 666111122 959121212 889121212 789456123
Persona
-String nombre -String apellido -String telfono
instance: Persona
nombre = Juan apellido = Lpez telfono = 666111122
[3]
HIBERNATE
Este tipo de sistema facilita todas las operaciones de persistencia y acceso a los datos desde la aplicacin al presentar las siguientes caractersticas principales: Independiente de SQL: Hibernate incorpora funcionalidades para las operaciones simples de recuperacin y actualizacin de datos para las cuales no ser necesario utilizar las sentencias SELECT, INSERT, UPDATE o DELETE habituales, siendo sustituidas por llamadas a mtodos (list, save, update, delete, ). Hibernate se encarga de generar la sentencia SQL que se encargar de realizar tales operaciones. De cualquier modo, se pueden especificar consultas SQL en caso necesario o incluso consultas en el dialecto de Hibernate, el HQL, con sintaxis similar a SQL pero que establece una serie de elementos propios del sistema ORM que gestiona. Independiente del SGBD: al aislar las funciones de manipulacin de datos del lenguaje SQL, se consigue que la aplicacin pueda comunicarse con cualquier SGBD ya que no existirn dependencias o particularidades en las sentencias de consulta o actualizacin de datos que haran a la aplicacin dependiente de un SGBD en cuestin. El encargado de realizar la traduccin final entre las operaciones Objeto-relacionales y las sentencias SQL es Hibernate, con lo cual el problema de la compatibilidad queda solventado. Incorpora soporte para la mayora de los sistemas de bases de datos existentes. Independiente de JDBC: Hibernate contiene una API completa que asla a la aplicacin, no solamente de las operaciones con lenguaje SQL, sino tambin la utilizacin de objetos propios de la gestin de Bases de datos JDBC (Statement, ResultSet, ). Este tipo de objetos habituales en la programacin Java de acceso a Bases de datos, se sustituye por el uso de objetos mucho ms sencillos como colecciones de clases tipo JavaBeans que contendrn los datos de los almacenes de datos.
[4]
HIBERNATE
En una arquitectura MVC genrica como la siguiente, Hibernate juega un papel primordial como capa de persistencia de la aplicacin. Como aparece en el esquema, los JavaBeans encargados de ejecutar la lgica de la aplicacin interacturn con datos habitualmente almacenados en una Base de datos. Hibernate se introduce en este contexto para dar soporte de acceso y gestin de los datos a la capa de Modelo.
Fuente: http://java.sun.com/blueprints/patterns/MVC-detailed.html
[5]
HIBERNATE
Controller (Controlador): Servlet central que recibe las peticiones, procesa la URL recibida y delega el procesamiento a los JavaBeans. Servlet que almacena el resultado del procesamiento realizado por los JavaBeans en el contexto de la peticin, la sesin o la aplicacin. Servlet que transfiere el control a un JSP que lleva a cabo la presentacin de resultados.
Model (Modelo): JavaBeans (o EJBs para aplicaciones ms escalables) que desempean el rol de modelo: Algunos beans ejecutan lgica. Otros guardan datos. Normalmente: El Servlet Controller invoca un mtodo en un bean de lgica y ste devuelve un bean de datos. El programador del JSP tiene acceso a beans de datos.
View (Vista): Rol ejecutado por JSPs. El Servlet Controller transfiere el control al JSP despus de guardar en un contexto el resultado en forma de un bean de datos. JSP usa jsp:useBean y jsp:getProperty para recuperar datos y formatear respuesta en HTML o XML.
En resumen: Los JavaBeans o EJBs ejecutan la lgica de negocio y guardan los resultados. Los JSPs proporcionan la informacin formateada. Los servlets coordinan/controlan la ejecucin de los beans y los JSPs.
[6]
HIBERNATE
Esta parte del manual es una gua paso a paso para realizar una aplicacin sencilla, es tal vez la mejor manera de introducirse con Hibernate y es recomendable realizarla antes de leer el resto del manual ya que mejorar la comprensin de ste. Esta aplicacin ser desarrollada haciendo uso del IDE Netbeans 6.7. Se puede descargar de http://netbeans.org/.
Estos son los pasos mnimos que se debern seguir para desarrollar la aplicacin satisfactoriamente. 1. Determinar el modelo de datos de la aplicacin. 2. Aadir las libreras Java de Hibernate. 3. Definir las clases de persistencia. 4. Crear los ficheros de mapeo. 5. Configurar Hibernate. 6. Crear clases de ayuda. 7. Cargar y guardar objetos. 8. Ejecutar la primera versin. 9. Asociar clases. 10. Ejecutar la aplicacin.
[7]
HIBERNATE
1. Determinar el modelo de datos de la aplicacin. Este ejemplo se trata de una sencilla introduccin donde nicamente se dispone de dos clases Event y Person tanto en la base de datos del sistema como en el modelo de objetos de la aplicacin. Tambin se usarn clases auxiliares para la gestin del acceso y operaciones con los datos. En primer lugar, es necesario crear la Base de datos en MySQL. Su nombre ser FirstApp pero no se van a crear tablas en la misma ya que se configurar una opcin en el fichero de configuracin de Hibernate (hibernate.cfg.xml) para que genere el esquema de la Base de datos al desplegar la aplicacin. Como se ha comentado, la aplicacin se crear a travs de Netbeans. Este entorno de desarrollo dispone de herramientas para la configuracin del acceso a la Base de datos. En primer lugar es posible que sea necesario configurar el driver de la base de datos que se va a utilizar en la aplicacin, en este caso MySQL. Para ello es necesario seleccionar la opcin New Connection del men del botn derecho del ratn sobre la opcin Drivers.
A continuacin se selecciona el fichero .jar que contiene el driver de MySQL y se dejan el resto de campos con los valores que aparecen al seleccionarlo. Todo lo relacionado
[8]
HIBERNATE
con la gestin de Drivers NO es propio de Hibernate, se est configurando el entorno de Netbeans para la utilizacin de los Asistentes existentes.
Ahora hay que crear la conexin desde la pestaa Services seleccionando la opcin New Connection del men del botn derecho del ratn sobre la opcin Databases.
[9]
HIBERNATE
Los datos que se completan son los correspondientes a la conexin con la base de datos que se encuentra en el servidor MySQL.
2. Aadir las libreras Java de Hibernate. Netbeans dispone de un asistente para la creacin de aplicaciones Web donde se puede indicar que la aplicacin emplear Hibernate. De esta forma, Netbeans realiza una serie de acciones de forma automtica que ayudarn al desarrollo mediante este Framework. Se deber crear un nuevo proyecto web con Netbeans, para ello dirigirse al men: File New Project Web Web Application. No se debe olvidar aadir el Framework, como muestran las imgenes siguientes.
[10]
HIBERNATE
Al crear el proyecto, Netbeans crea un fichero hibernate.cfg con los valores para conectar con la Base de datos que se ha establecido al crear el proyecto. Dicho archivo se encuentra en la carpeta Source Packages\default package de la aplicacin.
[11]
HIBERNATE
3. Definir las clases de persistencia. El paso siguiente es aadir clases para representar a los elementos de la Base de datos. La clase Event (paquete events) es una clase tipo JavaBean sencilla, cuyas propiedades coinciden con los campos de la tabla Events que existir en la Base de datos. package events; import java.util.Date; public class Event { private Long id; private String title; private Date date; public Event() { } public Long getId() { return id; } private void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
La propiedad id representa el identificador nico para un evento. Todas las clases de persistencia deben tener un identificador si se quiere utilizar el conjunto completo de
[12]
HIBERNATE
funcionalidades de Hibernate. El constructor sin argumentos tambin es un requisito para todas las clases persistentes. Se recomienda en la mayora de las ocasiones utilizar clases para representar los tipos de los atributos en lugar de utilizar tipos bsicos, debido a que para aquellos campos que se permitan valores nulos en la Base de datos, la representacin del valor del campo ser natural en la representacin Java (null), mientras que con tipos bsicos podra resultar en un error. En el paquete events tambin se introducir la clase Person, la cual se representar de forma similar por el momento: package events; public class Person { private Long id; private int age; private String firstname; private String lastname; public Person() { } // getters y setters para todas las propiedades }
4. Crear los ficheros de mapeo. Para cada una de las clases de persistencia tiene que existir un fichero de mapeo que se encarga de definir la relacin entre las tablas y columnas de stas en la Base de datos, y las clases y sus propiedades en el modelo de objetos. En Netbeans existe una utilidad para mapear las clases con las tablas de la Base de datos de forma automtica. Para ello, seleccionar la opcin New Other Hibernate Hibernate Mapping File
[13]
HIBERNATE
El nombre del fichero (Event.hbm.xml) coincidir con el nombre de la clase que mapea y se guardar habitualmente en el mismo paquete donde se encuentra dicha clase.
[14]
HIBERNATE
Solamente queda seleccionar la clase y la tabla a mapear. En este caso no se puede especificar ninguna tabla ya que la Base de datos se encuentra vaca.
Al pulsar Finish se genera un archivo de mapeo, al cual es necesario aadir el nombre de la tabla que est mapeando, y completar con los datos del mapeo de las columnas.
<?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="events.Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="native"/> </id> <property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/> </class> </hibernate-mapping>
[15]
HIBERNATE
Algunos detalles sobre este archivo: <class name="events.Event" table="EVENTS"> Aunque inicialmente el archive se crea con una serie de propiedades para el mapeo (dynamic-insert="false" dynamic-update="false" ) se han eliminado para simplificar el ejemplo. Realmente las nicas propiedades imprescindibles son name (nombre de la clase) y table (nombre de la tabla).
<id name="id" column="EVENT_ID"> En primer lugar se declara el identificador. Esta definicin se destaca porque se identifica con la etiqueta id. La propiedad name representa el nombre del atributo en la clase, y la propiedad column el nombre del campo en la tabla.
<generator class="native"/> Dentro del identificador se declara cmo se gestiona la asignacin de sta. En este caso se ha elegido el valor native, que delega la gestin de la clave en el SGBD utilizado, es decir, que no es necesario preocuparse de la gestin/asignacin de claves de forma manual. Existen otras posibilidades de generadores de clave que se estudiarn ms adelante en este documento.
[16]
HIBERNATE
<property name="date" type="timestamp" column="EVENT_DATE"/> La siguiente propiedad en declararse es date. En este caso tambin name representa el nombre del atributo en la clase, y column el nombre del campo en la tabla. La propiedad type representa el tipo de datos que se va a utilizar, ya que aunque Hibernate es capaz de detectar que este campo se refiere a un valor de tipo Date, es necesario especificar si esa fecha se gestionar como time, date o timestamp. Esta propiedad type sirve para realizar conversiones de tipos si es necesario en la aplicacin, de forma que un campo que en la base de datos es numrico se trate como String, o Boolean,
<property name="title"/> Por ltimo se declara el campo title. En este caso solamente se especifica el nombre del atributo de la clase porque el nombre coincide con el nombre de la columna de la tabla de Base de datos, si no sera necesario especificar el nombre de dicha columna.
El fichero de mapeo para la clase Person quedar de la siguiente forma: <?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="events.Person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="native"/> </id> <property name="age"/> <property name="firstname"/> <property name="lastname"/> </class> </hibernate-mapping>
[17]
HIBERNATE
5. Configurar Hibernate. Hasta el momento se han creado las clases para representar los objetos de la Base de datos y los ficheros de mapeo para las mismas. Para que la aplicacin funcione de forma correcta, es necesario configurar Hibernate a travs de su fichero de configuracin hibernate.cfg.xml. En este fichero se establecer al menos la conexin con la base de datos, y se especificarn cules son los ficheros de mapeo que se estn empleando en la aplicacin, es decir, que si un fichero de mapeo no aparece en hibernate.cfg el mapeo no funcionar. El fichero hibernate.cfg se encuentra en el paquete por defecto de la aplicacin. Si se abre este fichero con Netbeans, en primer lugar aparece la vista Diseo. Al seleccionar la vista XML (esquina superior izquierda de la ventana) aparecer el cdigo que Netbeans introdujo por defecto al crear la aplicacin y especificar la conexin a la Base de datos que se iba a emplear en la misma: <?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 name="session1"> <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/firstapp </property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <mapping resource="events/Event.hbm.xml"/> <mapping resource="events/Person.hbm.xml"/> </session-factory> </hibernate-configuration>
[18]
HIBERNATE
<session-factory name="session1"> Indica el session factory utilizado en la aplicacin. Si la aplicacin accede a ms de una Base de datos es necesario declarar ms de un session-factory, cada uno de ellos identificado por un nombre distinto.
<property name="hibernate.dialect"> La propiedad dialect indica la variante de SQL que Hibernate genera a la hora de acceder a la Base de datos.
Adems de los datos de la conexin a la Base de datos y los ficheros de mapeo de la aplicacin (que Netbeans ha introducido de forma automtica al crear los propios ficheros hbm), en hibernate.cfg deben aparecer propiedades importantes que definen los siguientes aspectos: current_session_context_class: Hibernate funciona con sesiones para acceder a los datos. Es necesario definir una clase de gestin de sesiones para poder realizar operaciones con la Base de datos. cache.provider_class: especifica el comportamiento de la Cach de 2 nivel. show_sql: mostrar en los logs las sentencias SQL que Hibernate lanza cada vez que realiza una operacin con la Base de datos. hbm2ddl.auto: crear el esquema de la Base de datos de forma automtica al desplegar la aplicacin. Los valores a aadir a hibernate.cfg para el ejemplo son los siguientes: <!-- Enable Hibernate's automatic session context management--> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create</property>
[19]
HIBERNATE
6. Crear clases de ayuda. Una vez que se ha configurado Hibernate, ya se puede escribir el cdigo Java para el acceso a los datos. Como se ha comentado anteriormente, Hibernate utiliza el concepto de sesin para acceder a los datos, de forma que cualquier operacin de consulta o manipulacin de los datos debe estar enmarcada en una sesin. Para que la gestin de sesiones quede aislada del resto de operaciones de acceso a datos, y para evitar la duplicidad de cdigo, es habitual generar una clase de ayuda que se encarga de la inicializacin de sesiones. Netbeans permite la generacin de esta clase a travs del men New Other Hibernate HibernateUtil. En este caso la clase HibernateUtil se incorporar al paquete util.
[20]
HIBERNATE
package util; import org.hibernate.cfg.Configuration; import org.hibernate.SessionFactory; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().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; } }
[21]
HIBERNATE
Cuando una clase de la aplicacin quiera acceder a la sesin actual, simplemente invocar al mtodo esttico getSessionFactory() (HibernateUtil.getSessionFactory()).
7. Cargar y guardar objetos. Para manipular los datos de la Base de datos se va a crear una clase EventManager en el paquete event para el almacenamiento y recuperacin de eventos:
public class EventManager { public void createAndStoreEvent(String title, Date theDate) { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Crear el evento Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); //Almacenarlo session.save(theEvent); //Confirmar transaccin session.getTransaction().commit(); } public List listEvents() { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Obtener la lista de eventos List result = session.createQuery("from Event").list(); //Confirmar transaccin session.getTransaction().commit(); return result; } }
[22]
HIBERNATE
Los mtodos que se han definido son los siguientes, los cuales se describen para comprobar la sencillez de las acciones que realizan: createAndStoreEvent: crea y almacena un evento realizando los siguientes pasos: 1. Acceder a la sesin actual: a travs del mtodo esttico getSessionFactory() de la clase HibernateUtil creada anteriormente en el proyecto. 2. Abrir una transaccin: utilizando la sesin actual. 3. Crear el objeto: de la forma habitual en Java. 4. Almacenar el objeto: simplemente invocando al mtodo save del interfaz Session (sesin actual). 5. Confirmar la transaccin: utilizando tambin la sesin actual. En la siguiente imagen se muestra un esquema del funcionamiento de Hibernate basado en sesiones:
listEvents: obtiene una lista de todos los eventos de la Base de datos realizando los siguientes pasos: 1. Acceder a la sesin actual: a travs del mtodo esttico getSessionFactory() de la clase HibernateUtil creada anteriormente en el proyecto. 2. Abrir una transaccin: utilizando la sesin actual. Las consultas a bases de datos tambin requieren de una transaccin en Hibernate.
[23]
HIBERNATE
3. Obtener el conjunto de resultados: se obtiene una coleccin (List) de instancias de la clase Event cuyos datos coincidirn con los datos de los registros existentes en la Base de datos. Existen varias formas de obtener datos de una tabla en Hibernate, en este caso se ha utilizado un Query HQL. Posteriormente se tratarn en profundidad los aspectos relacionados con los distintos tipos de consultas en Hibernate. 4. Confirmar la transaccin: utilizando tambin la sesin actual.
8. Ejecutar la primera versin. Para probar lo que se ha implementado hasta el momento se va a crear una sencilla aplicacin Web. Para ello en primer lugar se modificar la pgina index.jsp del proyecto para que funcione como formulario para introducir los datos: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>JSP Page</title> </head> <body> <form action="Servlet"> <table> <tr> <td>title</td><td><input type="text" name="title"></input></td> </tr> <tr> <td>date</td><td><input type="text" name="date"></input></td> </tr> <tr> <td><input type="submit"/></td> </tr> </table> </form> </body> </html>
[24]
HIBERNATE
A continuacin se crea el Servlet que se encargar de la lgica de la aplicacin. Para crear un Servlet Netbeans tambin dispone de un Asistente a travs de la opcin New Other Web Servlet
Su nombre ser Servlet y se almacenar en el paquete servlet. En la siguiente pantalla se dejan por defecto los parmetros que se aadirn al fichero web.xml.
[25]
HIBERNATE
public class Servlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { EventManager mng = new EventManager(); //Parsear la fecha Date date = new SimpleDateFormat("dd/MM/yyyy"). parse(request.getParameter("date")); mng.createAndStoreEvent(request.getParameter("title"), date); //Mostrar los datos almacenados this.mostrarTabla(out, mng.listEvents()); } catch (Exception ex) { out.println(ex); } finally { out.close(); } } private void mostrarTabla(PrintWriter out, List lista) { out.println("<table>"); Iterator it = lista.iterator(); //Iterar sobre todos los eventos while (it.hasNext()) { Event event = (Event) it.next(); out.println("<tr>"); out.println("<td>" + event.getTitle() + "</td>"); out.println("<td>" + event.getDate() + "</td>"); out.println("</tr>"); } out.println("</table>"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } }
[26]
HIBERNATE
9. Asociar clases. La asociacin entre clases es uno de los aspectos ms importantes de Hibernate, as como la asociacin entre tablas es la base del modelo relacional de Bases de datos. Este aspecto puede resultar complejo en algunas ocasiones. Posteriormente se tratarn en profundidad los aspectos relacionados con los distintos tipos de asociaciones en Hibernate. En los pasos 3 y 4 se definieron las clases Event y Person a travs de cdigo Java y ficheros de mapeo. Ahora es necesario modelar la relacin que existe entre ambas a travs de una asociacin Hibernate. La relacin que existe entre ambas tablas es una relacin M a M, de forma que a un Evento acudirn varias Personas, y una Persona puede acudir a diversos Eventos. A nivel de Bases de datos, esta relacin dar lugar a una tabla intermedia PERSON_EVENT. Esta relacin en Hibernate se puede modelar de diversas maneras, y adems se puede reflejar de distintas formas en las clases implicadas.
En primer lugar, la relacin ms sencilla que se puede establecer es la relacin Unidireccional entre 2 tablas, en este caso entre las tablas Person y Person_Event. Para el ejemplo, el tipo de relacin escogido es algo complejo, denominado en Hibernate como relacin con tabla intermedia (join table). En el captulo 4 se explicarn los tipos de relaciones ms generales, y la forma de eludir las relaciones complejas, pero es bueno presentar en el ejemplo otro tipos de relaciones que pueden encontrarse en aplicaciones Hibernate. Las relaciones se modelan con colecciones, normalmente conjuntos (Set) de forma que se agregan los fragmentos de cdigo que aparecen a continuacin. En la clase simplemente se crea el atributo de la clase Set y se generan los mtodos get/set para el mismo.
[27]
HIBERNATE
private Set events = new HashSet(); public Set getEvents() { return events; } public void setEvents(Set events) { this.events = events; }
En Person.hbm.xml se genera una nueva propiedad para la tabla de tipo conjunto. Los atributos de esa propiedad son los siguientes: name: nombre de la propiedad de la clase a la que hace referencia. table: tabla intermedia de la base de datos. key: clave de la tabla intermedia (PERSON_EVENT) que aporta la clase actual. La propiedad columna es el nombre de la columna de la tabla de Base de datos. Es decir, esta etiqueta significa que el nombre del campo que relaciona a la tabla que modela la clase Person (PERSON) y a la tabla intermedia (PERSON_EVENT) es PERSON_ID, el cual estar relacionado con el campo id de la clase Person (en este caso tambin se denomina PERSON_ID). many-to-many: indica para la tabla intermedia (PERSON_EVENT) cul es la otra clase que est implicada en la relacin (la clase Event) y cul es la clave fornea que esta clase aporta a la relacin.
Con todo esto nicamente se ha posibilitado que a travs de una persona concreta, es decir, una instancia de la clase Person, se pueda conocer de forma directa los eventos a los cuales est asociada. En Hibernate esto se traduce en acceder simplemente a los atributos de dicha clase. Por ejemplo, el mtodo getEvents devuelve un Set con los eventos asociados a una persona:
[28]
HIBERNATE
Esta utilidad es suficiente, pero se pueden realizar cosas ms avanzadas, como manipular objetos persistentes y luego transformarlos en acciones contra la base de datos. Por ejemplo, el siguiente mtodo asigna un evento a una persona de forma sencilla, sin gestionar claves ni relaciones entre tablas, simplemente a travs de la relacin normal entre conjuntos: public void addPersonToEvent(Long personId, Long eventId) { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Cargar la persona por clave Person aPerson = (Person) session.load(Person.class, personId); //Cargar el evento por clave Event anEvent = (Event) session.load(Event.class, eventId); //Agregar el evento a la persona aPerson.getEvents().add(anEvent); //Confirmar transaccin session.getTransaction().commit(); }
La lnea aPerson.getEvents().add(anEvent) es donde se crea la relacin entre ambos elementos, y al realizar la confirmacin en la Base de datos se crean los registros necesarios para satisfacer dicha relacin. A continuacin se va a modelar la relacin inversa. Una relacin en Hibernate se puede modelar de forma unidireccional o bidireccional. Para el segundo de los casos, se establecen en los ficheros de configuracin 2 relaciones, por una parte la relacin directa (que corresponde con la que ya se ha generado en el ejemplo), y por la otra parte se modela la relacin inversa. Es decir, si en el ejemplo la relacin directa serva para conocer cules eran los eventos a los que estaba asociada una persona, la relacin inversa proporciona informacin sobre cules son las personas que participan en un determinado evento.
[29]
HIBERNATE
Para ello, en la clase Event se establece tambin una propiedad de tipo Set que almacenar los datos de las personas asociadas a un evento concreto:
public void setParticipants(Set participants) { this.participants = participants; } El mapeo es muy similar al que se estableci para la clase Person, teniendo en cuenta que hay que incluir la propiedad inverse (con valor true): name: nombre de la propiedad de la clase a la que hace referencia. table: tabla intermedia de la base de datos. inverse: en toda relacin bidireccional uno de los 2 extremos debe ser inverse=true, lo cual es utilizado por Hibernate a la hora de construir las sentencias SQL para interactuar con la Base de datos. key: el nombre del campo que relaciona a la tabla que modela la clase Event (EVENT) y a la tabla intermedia (PERSON_EVENT) es EVENT_ID, el cual estar relacionado con el campo id de la clase Event (EVENT_ID). many-to-many: indica para la tabla intermedia (PERSON_EVENT) cul es la otra clase que est implicada en la relacin (la clase Person) y cul es la clave fornea que esta clase aporta a la relacin (PERSON_ID).
<set name="participants" table="PERSON_EVENT" inverse="true"> <key column="EVENT_ID"/> <many-to-many column="PERSON_ID" class="events.Person"/> </set>
[30]
HIBERNATE
Gracias a esta nueva relacin se pueden conocer las personas que pertenecen a un determinado evento. La pregunta es, funcionar correctamente un mtodo como el siguiente? public void addPersonToEvent(Long personId, Long eventId) { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Cargar la persona por clave Person aPerson = (Person) session.load(Person.class, personId); //Cargar el evento por clave Event anEvent = (Event) session.load(Event.class, eventId); //Agregar la persona al evento anEvent.getParticipants().add(aPerson); //Confirmar transaccin session.getTransaction().commit(); }
10. Ejecutar la aplicacin. Para probar todos los conceptos vistos hasta el momento, se crea un nuevo Servlet de la misma forma especificada en el paso 8, donde se realizarn las siguientes pruebas: Cargar eventos por clave (emng.loadEvent(new Long(1))), para comprobar si existe un evento con identificador igual a 1. Almacenar personas (mng.createAndStorePerson(): almacena una persona de prueba. Agregar personas a eventos (mng.addPersonToEvent(new Long(1), e.getId())): agrega la persona creada anteriormente al evento. Listar participantes de un evento (e.getParticipants()): obtiene la lista de participantes al evento para mostrar sus nombres y la lista de eventos.
[31]
HIBERNATE
public class ServletP extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { PersonManager mng = new PersonManager(); //Comprobar que existe el evento con id=1 EventManager emng = new EventManager(); Event e = emng.loadEvent(new Long(1)); if (e == null) { //No existe el evento out.println("Debe crear un evento"); } else { //Almacenar mng.createAndStorePerson(new Integer(30), "Jos", "Guardiola");
//Mostrar los datos almacenados en el evento this.mostrarTabla(out, e.getParticipants()); } } catch (Exception ex) { out.println(ex); } finally { out.close(); } }
[32]
HIBERNATE
private void mostrarTabla(PrintWriter out, Set lista) { out.println("<table>"); Iterator it = lista.iterator(); //Iterar sobre todos los eventos while (it.hasNext()) { Person p = (Person) it.next(); out.println("<tr>"); out.println("<td>" + p.getAge() + "</td>"); out.println("<td>" + p.getFirstname() + "</td>"); out.println("<td>" + p.getLastname() + "</td>"); out.println("<td>" + p.getEvents() + "</td>"); out.println("</tr>"); } out.println("</table>"); } }
3. Tras la ejecucin del cdigo del Servlet se muestra la lista de participantes en el evento inicial. Para el ejemplo, la persona que se almacena como prueba estar al menos en el evento creado inicialmente.
[33]
HIBERNATE
El cdigo del Servlet resulta sencillo ya que toda la lgica de interaccin con Hibernate se encuentra en las clases PersonManager y EventManager, destacando la forma en que se han realizado las operaciones basadas en relaciones: Agregar personas a eventos (mng.addPersonToEvent(new Long(1), e.getId())): la persona se agrega al evento a travs del conjunto de eventos en los que participa una persona (ver PersonManager). Listar participantes de un evento (e.getParticipants()): los participantes a un evento se obtienen a travs del conjunto de personas que participa en un evento. Para ello se carga el evento, se obtienen sus participantes, y para cada uno de ellos se muestra su nombre y la lista de eventos en los cuales participa. El mtodo loadPerson carga una persona a travs del valor de su campo clave (id). Para ello se utiliza el mtodo load del interfaz Session. El procedimiento de recuperacin de una instancia tambin sigue el esquema de sesiones y transacciones explicado con anterioridad. El mtodo listPerson devuelve un listado de todas las personas a travs de un Criteria. Esta forma de recuperar datos se explica en el captulo 5.2 con detenimiento, pero por el momento destacar que el mtodo setFetchMode posibilita la recuperacin de datos relacionados, es decir, Hibernate por defecto no cargar los datos de los conjuntos que estn relacionados con una instancia (por ejemplo, los eventos a los que est asociada una persona) para no sobrecargar innecesariamente al sistema, as que en caso de querer recuperarlos hay que notificarlo de esta forma, entre otras.
[34]
HIBERNATE
/** * Almacenar un evento * @param title ttulo * @param theDate fecha */ public void createAndStoreEvent(String title, Date theDate) { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Crear el evento Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); //Almacenarlo session.save(theEvent); //Confirmar transaccin session.getTransaction().commit(); }
/** * Cargar un evento * @return evento */ public Event loadEvent(Long eventId) { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Cargar el evento por clave
[35]
HIBERNATE
Event anEvent = (Event) session.createCriteria(Event.class). setFetchMode("participants", FetchMode.JOIN). setFetchMode("participants.events", FetchMode.JOIN). add(Restrictions.eq("id", eventId)). uniqueResult(); //Confirmar transaccin session.getTransaction().commit(); return anEvent; }
/** * Listar los eventos * @return lista de eventos */ public List listEvents() { //Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); //Comenzar la transaccin session.beginTransaction(); //Obtener la lista de personas List result = session.createCriteria(Event.class). setFetchMode("participants", FetchMode.JOIN). list(); //Confirmar transaccin session.getTransaction().commit(); return result; } }
El mtodo loadEvent carga un evento a travs del valor de su campo clave (id). Para ello se utiliza un Criteria al cual se le aade una restriccin de igualdad (Restrictions.eq) para filtrar por el campo clave (id). Adems se utilizan los mtodos necesarios para que se carguen los participantes de un evento
[36]
HIBERNATE
(setFetchMode("participants", FetchMode.JOIN)) y para cada uno de dichos participantes, que en su propiedad events (ver clase Person) se carguen los datos de sus eventos (setFetchMode("participants", FetchMode.JOIN)). De esta forma, para cada evento se podran conocer los datos completos de las personas asociadas (edad, nombre, apellidos y eventos a los que se encuentra asociada). El mtodo uniqueResult indica que la consulta deber devolver un nico valor, el cual es una instancia de la clase solicitada. Para estas 2 clases, los ficheros de mapeo quedan configurados segn los cambios que se han comentado al introducir las relaciones entre ambas. Para ejecutar el proyecto simplemente queda modificar la pgina JSP de inicio, para que incluya la opcin Prueba relaciones: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>JSP Page</title> </head> <body> <form action="Servlet"> <table> <tr> <td>title</td><td><input type="text" name="title"></input></td> </tr> <tr> <td>date</td><td><input type="text" name="date"></input></td> </tr> <tr> <td><input type="submit"/></td> </tr> </table> </form> <a href="ServletP">Prueba relaciones</a> </body> </html>
[37]
HIBERNATE
3. FRAMEWORK HIBERNATE
3.1.INTRODUCCIN
En el desarrollo de una aplicacin J2EE se trabaja continuamente con objetos. Sin embargo, las bases de datos relacionales no trabajan con ellos, sino con conjuntos y relaciones entre conjuntos. Entonces Cmo se almacena un objeto en una base de datos? Y cmo se mantienen las relaciones cuando se convierte un elemento de una base de datos en un objeto Java? La causa de esta disparidad es la llamada diferencia Objeto-Relacional. El problema se agrava cuanto ms complejo sea el material a trasladar de un paradigma a otro. En estos casos se hace necesario un puente entre ambos conceptos. Es lo que se llama un mapeador objeto-relacional, tambin conocido por sus siglas en ingls ORM (Object-Relational Mapping). Un ORM permite al programador aislarse de los detalles bsicos. Basta definir cmo se deben trasladar los datos entre ambos modelos y el mapeador se encargar de aplicarlo en cada ocasin. Hibernate es un potente servicio de consulta y persistencia objeto/relacional con el cual es posible desarrollar clases persistentes mediante lenguajes orientados a objetos manteniendo sus caractersticas bsicas (incluyendo herencia, polimorfismo, colecciones, etc...). Permite expresar consultas tanto en SQL como en su propia extensin de este lenguaje, denominada HSQL. Hibernate es un proyecto de software libre con cdigo abierto, por lo que su desarrollo se encuentra en continua evolucin. La pgina del proyecto: http://www.hibernate.org/
[38]
HIBERNATE
3.2.INSTALACIN
IMPORTANTE: Este apartado sirve como gua de ayuda de instalacin manual de Hibernate para su uso dentro de una aplicacin. No es recomendable realizar la instalacin de Hibernate a mano. Normalmente, se utilizan entornos de desarrollo integrados como Netbeans, con los que se puede elegir durante la creacin del proyecto que aada los frameworks deseados, entre ellos Hibernate, de forma que el entorno los configurar adecuadamente de forma automtica. En caso de que se descargue la ltima versin, se recomienda seguir la ayuda oficial ya que estas instrucciones pueden estar obsoletas.
1. Descargar la distribucin de Hibernate Core de: https://www.hibernate.org/344.html 2. Descomprimir el fichero 3. Copiar los archivos jar necesarios a la carpeta lib de nuestra aplicacin: a. hibernate3.jar b. \lib\required\*.jar c. \lib\optional\*.jar 4. Crear el fichero hibernate.cfg.xml en el paquete por defecto de la aplicacin.
3.3.CONFIGURACIN
Hibernate se configura a travs hibernate.cfg.xml. En el paso 5 del ejemplo anterior se explicaron algunos de los elementos de configuracin que se pueden encontrar en este fichero:
[39]
HIBERNATE
<?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 name="session1"> <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/firstapp </property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property>
<!-- Enable Hibernate's automatic session context management--> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class"> org.hibernate.cache.NoCacheProvider </property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create</property>
[40]
HIBERNATE
Este archivo es ledo por el framework cuando arranca y contiene los siguientes elementos: Sesiones: session factory utilizados en la aplicacin. Si la aplicacin accede a ms de una Base de datos es necesario declarar ms de un session-factory, cada uno de ellos identificado por un nombre distinto.
Propiedades: dentro de cada sesin se declaran una serie propiedades para la conexin y configuracin del acceso a la Base de datos:
Driver JDBC de la Base de datos URL de conexin a la Base de datos Usuario Clave Mximo nmero de conexiones del pool
Profundidad mxima de joins para relaciones simples Tamao por defecto para el fetching de asociaciones Modo por defecto para la representacin de entidades Ordenar las actualizaciones por clave primaria Generar estadsticas de rendimiento Resetea el identificador asociado en caso de eliminacin Genera comentarios en las sentencias SQL dynamic-map dom4j pojo true false true false true false true false
hibernate.use_sql_comments
[41]
HIBERNATE
hibernate.jdbc.fetch_size hibernate.jdbc.batch_size
Tamao de bsqueda Actualizacin batch de JDBC2 Utilizar el rowcount que devuelve true executeBatch() false Seleccionar un org.hibernate.jdbc.Batcher propio Uso de resultsets scrollables de JDBC2 Uso de streams para la escritura de tipos de datos serializables Uso de getGeneratedKeys() de JDBC3 Uso de un ConnectionProvider propio Habilita el Autocommit true false true false true false true false
hibernate.jdbc.batch_versioned_data
hibernate.jdbc.factory_class
hibernate.jdbc.use_scrollable_resultset
hibernate.jdbc.use_streams_for_binary
hibernate.jdbc.use_get_generated_keys
hibernate.connection.provider_class
hibernate.connection.autocommit
hibernate.connection.release_mode
auto Modo de Liberacin de conexiones on_close JDBC after_transaction after_statement Propiedad especfica de JDBC para la conexin Propiedad especfica de JNDI para la conexin Nivel de aislamiento de transacciones
hibernate.connection.<propertyName>
hibernate.jndi.<propertyName>
hibernate.connection.isolation
[42]
HIBERNATE
Propiedades de cach: gestin de la cach de segundo nivel. Nombre de CacheProvider propio. Optimizar la operacin de la cach al mnimo nmero de escrituras. Habilitar la cache de consultas Deshabilitar completamente la cache Nombre de un interfaz QueryCache propio Prefijo de los nombres regionales de la cach true false true false true false true false
hibernate.cache.provider_class
hibernate.cache.use_minimal_puts
hibernate.transaction.factory_class
Nombre de la factora de transacciones Nombre para obtener la transaccin JTA Nombre del TransactionManagerLookup La sesin se lanza antes de concluir la transaccin true false
jta.UserTransaction
hibernate.transaction.manager_lookup_class
hibernate.transaction.flush_before_completion
hibernate.transaction.auto_close_session
[43]
HIBERNATE
Otras propiedades.
hibernate.query.factory_class
hibernate.query.substitutions
Mapeo a consultas SQL validate Genera el esquema update automticamente al create crear la Sesin create-drop Utilizacin de CGLIB true en lugar de reflexin false
hibernate.hbm2ddl.auto
hibernate.cglib.use_reflection_optimizer
3.3.1. MAPEOS
Los ficheros de mapeo que se utilizan en la aplicacin se definen en hibernate.cfg, indicando la ruta donde se encuentran los mismos. Cada uno de estos ficheros con extensin hbm.xml mapean una clase de la aplicacin con una de las tablas de la Base de datos especificada en la conexin, y contienen los detalles de dicho mapeo.
[44]
HIBERNATE
Los ficheros de mapeo normalmente incluyen el nombre de la tabla y clase relacionadas, y el detalle del mapeo entre columnas de la tabla y propiedades de la clase, como puede observarse en la ilustracin anterior. Un fichero de mapeo puede contener varias clases mapeadas, aunque habitualmente se realiza con una sola clase (cuyo nombre debera coincidir con el nombre del fichero de mapeo). Los elementos bsicos de un fichero de mapeo aparecen a continuacin. Se marcarn en negrita aquellos elementos importantes para su utilizacin: 1. hibernate-mapping: datos generales del mapeo.
<hibernate-mapping schema="schemaName" (a) catalog="catalogName" (b) default-cascade="cascade_style" (c) default-access="field|property|ClassName" (d) default-lazy="true|false" (e) auto-import="true|false" (f) package="package.name" (g) />
a. schema (opcional): nombre del esquema de bases de datos. b. catalog (opcional): nombre del catlogo de la base de datos. c. default-cascade (opcional none por defecto): estilo de cascada por defecto.
[45]
HIBERNATE
d. default-access (opcional property por defecto): estrategia de acceso a las propiedades. e. default-lazy (opcional true por defecto): valor por defecto del atributo lazy de los mapeos de clases y colecciones. f. auto-import (opcional true por defecto): permite usar nombres de clases sin calificar en el lenguaje SQL. Si hay dos clases con el mismo nombre sin calificar, esta propiedad tiene que tener el valor false. g. package (opcional): prefijo de paquete para los nombres de clases sin cualificar.
2. class: datos de la clase persistente y su relacin con la tabla de la base de datos. <class name="ClassName" (a) table="tableName" (b) discriminator-value="discriminator_value" (c) mutable="true|false" (d) schema="owner" (e) catalog="catalog" (f) proxy="ProxyInterface" (g) dynamic-update="true|false" (h) dynamic-insert="true|false" (i) select-before-update="true|false" (j) polymorphism="implicit|explicit" (k) where="arbitrary sql where condition" (l) persister="PersisterClass" (m) batch-size="N" (n) optimistic-lock="none|version|dirty|all" (o) lazy="true|false" (p) entity-name="EntityName" (q) check="arbitrary sql check condition" (r) rowid="rowid" (s) subselect="SQL expression" (t) abstract="true|false" (u) node="element-name" />
[46]
HIBERNATE
a. name (opcional): nombre completo de la clase o interfaz Java. Tambin se pueden hacer persistentes clases estticas internas. b. table (opcional por defecto el nombre de la clase sin cualificar): nombre de la tabla de la base de datos. c. discriminator-value (opcional por defecto el nombre de la clase): distingue subclases individuales, se usa para comportamiento polimrfico. d. mutable (opcional true por defecto): especifica si las instancias son mutables o no. Las clases inmutables no se pueden modificar ni eliminar. e. schema (opcional): sobreescribe el nombre de esquema especificado por <hibernate-mapping>. f. catalog (opcional): sobreescribe el nombre de catlogo especificado por <hibernate-mapping>. g. proxy (opcional): interfaz de proxy para la incializacin perezosa. h. dynamic-update (opcional false por defecto): la sentencia SQL UPDATE se genera en tiempo de ejecucin y solamente contiene las columnas cuyos valores hayan cambiado. i. dynamic-insert (opcional false por defecto): la sentencia SQL INSERT se genera en tiempo de ejecucin y solamente contiene las columnas cuyos valores son distintos a null. j. select-before-update (opcional false por defecto): se produce una sentencia SELECT antes de actualizar para comprobar si un objeto ha sido modificado. Si no no se produce dicha actualizacin. k. polymorphism (opcional implicit por defecto): determina si se utiliza polimorfismo implcito o explcito a la hora de recuperar objetos en las consultas. l. where (opcional): condicin WHERE a utilizar cuando se recuperan objetos de esta clase. m. persister (opcional): especifica un ClassPersister propio, permitiendo la redefinicin de la estrategia de almacenamiento persistente. n. batch-size (opcional 1 por defecto): tamao batch para la bsqueda de instancias de esta clase por campo clave.
[47]
HIBERNATE
o. optimistic-lock (opcional version por defecto): determina la estrategia de bloqueo optimista. p. lazy (opcional): bsqueda perezosa. q. entity-name (opcional por defecto el nombre de la clase): Hibernate3 permite mapear una misma clase distintas veces (con distintas tablas), y este nombre ser el utilizado. r. check (opcional): expression SQL para generar una comprobacin en la generacin automtica de esquemas. s. rowid (opcional): utilizacin de ROWID. t. subselect (opcional): mapea una entidad immutable de solo lectura con una subconsulta de la base de datos, por ejemplo para usar vistas en lugar de tablas. u. abstract (opcional): marca superclases abstractas en jerarquas con <unionsubclass>. 3. id: dentro de la etiqueta class, se debe mapear la clave primaria de la tabla.
<id name="propertyName" (a) type="typename" (b) column="column_name" (c) unsaved-value="null|any|none|undefined|id_value" (d) access="field|property|ClassName"> (e) node="element-name|@attribute-name|element/@attribute|." <generator class="generatorClass"/> </id>
a. name (opcional): nombre de la propiedad. b. type (opcional): indica el tipo. c. column (opcional nombre de la propiedad por defecto): nombre de la columna.
[48]
HIBERNATE
d. unsaved-value (opcional): valor del identificador que indica que la instancia es nueva, para distinguir de instancias guardadas o cargadas en sesiones anteriores. Casi nunca se utiliza en Hibernate 3. e. access (opcional property por defecto): estrategia de acceso al valor de la propiedad. 4. generator: dentro del id, es necesario definir un generador de identificadores nicos para la clase.
a. class:
nombre
de
la
clase
que
implementa
el
interfaz
org.hibernate.id.IdentifierGenerator, a la cual se pueden pasar parmetros a travs de los elementos <param>. Asimismo, existe una serie de clases predefinidas: i. increment: identificadores nicos siempre que no existan otros procesos insertando en la tabla. ii. identity: columna de tipo identity (autonumrica) para aquellos sistemas que la soportan. iii. sequence: asignacin basada en secuencia para aquellos sistemas que la soportan. iv. hilo: genera identificadores con un algoritmo hi/lo dado un nombre de tabla y de columna. v. seqhilo: genera identificadores con un algoritmo hi/lo dado un nombre de secuencia. vi. uuid: genera claves de tipo uuid de tamao 32. vii. guid: usa una cadena guid generada por la base de datos para aquellos sistemas que la soportan.
[49]
HIBERNATE
viii. native: selecciona identity, sequence o hilo dependiendo de la base de datos en uso. ix. assigned: asignado por la aplicacin. Es la opcin por defecto si no se incluye generator. x. select: asignacin por trigger. xi. foreign: usa el identificador de otro objeto asociado, normalmente a travs de un <one-to-one>. xii. sequence-identity: usa una secuencia y el mtodo getGeneratedKeys de JDBC3 para la generacin.
5. composite-id: utilizado en el caso que la clave primaria est compuesta por ms de una columna. <composite-id name="propertyName" (a) class="ClassName" (b) mapped="true|false" (c) access="field|property|ClassName"> (d) node="element-name|." <key-property name="propertyName" type="typename" column="column_name"/> (e) <key-many-to-one name="propertyName class="ClassName" column="column_name"/> (f) ...... </composite-id>
a. name (opcional): nombre de la propiedad. b. class (opcional): la forma de implementar una clave compuesta en Hibernate es a travs de una clase. Esta propiedad indica el nombre de dicha clase, que contendr atributos para cada una de las columnas que componen la clave. c. mapped (opcional false por defecto): utilizacin de un identificador compuesto mapeado. d. access (opcional property por defecto): estrategia de acceso al valor de la propiedad.
[50]
HIBERNATE
e. key-property: dentro de la etiqueta composite-id, indica cada uno de los campos que forman la clave y que no corresponden a claves forneas de otras tablas: i. name: nombre de la propiedad (de la clase que implementa el identificador). ii. type (opcional): tipo Hibernate de la propiedad. iii. column (opcional nombre de la propiedad por defecto): nombre de la columna. f. key-many-to-one: dentro de la etiqueta composite-id, indica cada uno de los campos que forman la clave y que corresponden a claves forneas de otras tablas: i. name: nombre de la propiedad (de la clase que implementa el identificador). ii. class: nombre de la clase relacionada a travs de la clave fornea. iii. column (opcional nombre de la propiedad por defecto): nombre de la columna. 6. property: declaracin de propiedades de la clase. <property name="propertyName" (a) column="column_name" (b) type="typename" (c) update="true|false" (d) insert="true|false" (e) formula="arbitrary SQL expression" (f) access="field|property|ClassName" (g) lazy="true|false" (h) unique="true|false" (i) not-null="true|false" (j) optimistic-lock="true|false" (k) generated="never|insert|always" (l) node="element-name|@attribute-name|element/@attribute|." index="index_name" unique_key="unique_key_id" length="L" precision="P" scale="S" />
[51]
HIBERNATE
a. name: nombre de la propiedad (primera letra siempre en minuscule). b. column (opcional nombre de la propiedad por defecto): nombre de la columna de base de datos mapeada. Tambin puede ser especificado a travs de etiquetas <column>. c. type (opcional): indica el tipo Hibernate, que puede tratarse de uno de los siguientes, aunque en caso de no especificar Hibernate lo asignar automticamente en funcin del tipo de la columna de base de datos: i. Tipo bsico Hibernate (integer, string, character, date, ). ii. Clase Java o tipo bsico (int, float, java.util.Date, ). iii. Clase Java serializable. iv. Clase propia. d. update (opcional true por defecto): las columnas mapeadas deben incluirse en las sentencias UPDATE. e. insert (opcional true por defecto): las columnas mapeadas deben incluirse en las sentencias INSERT. f. formula (opcional): expresin SQL que define el valor de una propiedad calculada. Las propiedades calculadas no se mapean a ninguna columna. g. access (opcional property por defecto): estrategia utilizada para acceder a la propiedad. h. lazy (opcional false por defecto): indica si esta propiedad debe ser buscada de forma perezosa cuando la instancia se accede por vez primera. i. j. unique (opcional): restriccin unique para esta columna. not-null (opcional): restriccin not-null para esta columna.
k. optimistic-lock (opcional true por defecto): las actualizaciones de esta propiedad requieren la adquisicin del bloqueo optimista. l. generated (opcional never por defecto): esta propiedad es generada por la base de datos. 7. many-to-one
[52]
HIBERNATE
<many-to-one name="propertyName" (a) column="column_name" (b) class="ClassName" (c) cascade="cascade_style" (d) fetch="join|select" (e) update="true|false" (f) insert="true|false" (g) property-ref="propertyNameFromAssociatedClass" (h) access="field|property|ClassName" (i) unique="true|false" (j) not-null="true|false" (k) optimistic-lock="true|false" (l) lazy="proxy|no-proxy|false" (m) not-found="ignore|exception" (n) entity-name="EntityName" (o) formula="arbitrary SQL expression" (p) node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" index="index_name" unique_key="unique_key_id" foreign-key="foreign_key_name" /> a. name: nombre de la propiedad. b. column (opcional): nombre de la columna de clave fornea. Tambin puede ser especificado a travs de etiquetas <column>. c. class (opcional): nombre de la clase asociada. d. cascade (opcional): operaciones que deben ejecutarse en cascada desde el padre al objeto asociado. e. fetch (opcional select por defecto): elige entre bsqueda mediante outerjoin o select secuencial. f. update (opcional true por defecto): las columnas mapeadas deben incluirse en las sentencias UPDATE. g. insert (opcional true por defecto): las columnas mapeadas deben incluirse en las sentencias INSERT.
[53]
HIBERNATE
h. property-ref (opcional): nombre de la propiedad de la clase asociada relacionada con esta foreign key. Si no se especifica se utiliza la clave de la clase asociada. i. access (opcional property por defecto): estrategia utilizada para acceder a la propiedad. j. unique (opcional): restriccin unique para la clave fornea.
k. not-null (opcional): restriccin not-null para la clave fornea. l. optimistic-lock (opcional true por defecto): las actualizaciones de esta propiedad requieren la adquisicin del bloqueo optimista. m. lazy (optional proxy por defecto): indica si esta propiedad debe ser buscada de forma perezosa cuando la instancia se accede por vez primera. n. not-found (opcional exception por defecto): cmo se tratan las claves forneas que referencian a filas inexistentes. o. entity-name (opcional): nombre de entidad de la clase asociada. p. formula (opcional): expresin SQL que define el valor de una propiedad calculada.
8. one-to-one <one-to-one name="propertyName" (a) class="ClassName" (b) cascade="cascade_style" (c) constrained="true|false" (d) fetch="join|select" (e) property-ref="propertyNameFromAssociatedClass" (f) access="field|property|ClassName" (g) formula="any SQL expression" (h) lazy="proxy|no-proxy|false" (i) entity-name="EntityName" (j) node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" foreign-key="foreign_key_name" />
[54]
HIBERNATE
c. cascade (opcional): operaciones que deben ejecutarse en cascada desde el padre al objeto asociado. d. constrained (opcional): una clave fornea de la clave primaria de la tabla mapeada referencia a la tabla de la clase asociada. e. fetch (opcional select por defecto): elige entre bsqueda mediante outerjoin o select secuencial. f. property-ref (opcional): nombre de la propiedad de la clase asociada relacionada con esta foreign key. Si no se especifica se utiliza la clave de la clase asociada. g. access (opcional property por defecto): estrategia utilizada para acceder a la propiedad. h. formula (opcional): expresin SQL que define el valor de una propiedad calculada. i. lazy (optional proxy por defecto): indica si esta propiedad debe ser buscada de forma perezosa cuando la instancia se accede por vez primera. j. entity-name (opcional): nombre de entidad de la clase asociada.
9. set: las relaciones inversas normalmente se modelan a travs de un conjunto (set), pero existen otros elementos que se pueden usar como list, map, bag, array o primitive-array.
[55]
HIBERNATE
<set name="propertyName" (a) table="table_name" (b) schema="schema_name" (c) lazy="true|extra|false" (d) inverse="true|false" (e) cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan" (f) sort="unsorted|natural|comparatorClass" (g) order-by="column_name asc|desc" (h) where="arbitrary sql where condition" (i) fetch="join|select|subselect" (j) batch-size="N" (k) access="field|property|ClassName" (l) optimistic-lock="true|false" (m) mutable="true|false" (n) node="element-name|." embed-xml="true|false" > <key .... /> (o) <map-key .... /> <element .... /> </map>
a. name: nombre de la propiedad. b. table (opcional nombre de la propiedad por defecto): nombre de la tabla de la coleccin (no se usa para one-to-many). c. schema (opcional): nombre del esquema para sobreescribir el esquema definido en el elemento raz. d. lazy (opcional true por defecto). e. inverse (opcional false por defecto): indica el lado inverso en una asociacin bidireccional. f. cascade (opcional - none por defecto): permite operaciones en cascada sobre los elementos hijos. g. sort (opcional): especifica una coleccin ordenada mediante orden natural o especificando una clase comparadora.
[56]
HIBERNATE
h. order-by (opcional): solo en JDK 1.4, especifica un nombre de columna que define el orden de iteracin. i. where (opcional): condicin WHERE a utilizar cuando se recuperan o eliminan objetos de la coleccin. j. fetch (opcional select por defecto): elige entre bsqueda mediante outerjoin o select secuencial. k. batch-size (opcional 1 por defecto): tamao batch para la bsqueda de instancias de esta clase por campo clave. l. access (opcional property por defecto): estrategia utilizada para acceder a la propiedad. m. optimistic-lock (opcional true por defecto): las actualizaciones del estado de la coleccin requieren la adquisicin del bloqueo optimista. n. mutable (opcional true por defecto): los elementos de la coleccin nunca cambian. o. key: dentro de la etiqueta set indica la clave fornea de la coleccin:
<key column="columnname" (i) on-delete="noaction|cascade" (ii) property-ref="propertyName" (iii) not-null="true|false" (iv) update="true|false" (v) unique="true|false" (vi) />
i. column (opcional): nombre de la columna que acta como clave fornea. Tambin puede especificarse a travs de elementos <column>. ii. on-delete (opcional noaction por defecto): la clave fornea tiene activado el borrado en cascada en la base de datos. iii. property-ref (opcional): la clave fornea se refiere a columnas que no son la clave primaria de la tabla original. iv. not-null (opcional): las claves forneas no se pueden establecer como null.
[57]
HIBERNATE
v. update (opcional): las claves forneas no se pueden actualizar. vi. unique (opcional): la clave fornea debera tener una restriccin unique.
p. one-to-many: indica
<one-to-many class="ClassName" (i) not-found="ignore|exception" (ii) entity-name="EntityName" (iii) node="element-name" embed-xml="true|false" />
i. class: nombre de la clase asociada. ii. not-found (opcional exception por defecto): cmo se tratan las claves forneas que referencian a filas inexistentes. iii. entity-name (opcional): nombre de entidad de la clase asociada.
El interfaz Session es la principal va de interaccin entre la aplicacin Java e Hibernate. Representa a la sesin actual, donde se producen transacciones para la interaccin con la Base de datos, efectuando operaciones de lectura, creacin, modificacin y eliminacin de instancias de clases mapeadas. Las instancias de tales clases se pueden encontrar en tres estados posibles: transient: no es persistente, no est asociada a ninguna sesin. Se pueden convertir en persistent invocando a los mtodos save, persist o saveOrUpdate. Se encuentra en la aplicacin pero no en la Base de datos. persistent: asociado a una nica sesin. Se encuentra en la aplicacin y en la Base de datos.
[58]
HIBERNATE
detached: persistente previamente, no asociado con ninguna sesin. Se encuentra en la Base de datos, pero no se ha recuperado desde la aplicacin.
En la siguiente ilustracin aparecen estos estados, y cules son los mtodos que provocan la transicin de uno a otro.
Para poder operar con una sesin es necesario obtener una instancia de la misma y comenzar una transaccin. Cualquier operacin sobre dicha instancia debe contenerse dentro de una transaccin. Si se produce una excepcin dentro de la transaccin, sta debe ser cancelada (rollback), si no hay que confirmarla para hacer persistentes los cambios (commit):
[59]
HIBERNATE
//Obtener la sesin actual Session session = HibernateUtil.getSessionFactory().getCurrentSession(); try{ //Comenzar la transaccin session.beginTransaction(); //Operaciones con session }catch (Exception ex){ //Deshacer transaccin session.getTransaction().rollback(); }finally{ //Confirmar transaccin session.getTransaction().commit(); }
Los mtodos que se invocan sobre la instancia de Session producen operaciones sobre la Base de datos a travs de sentencias SQL, generadas en el dialecto concreto configurado en hibernate.cfg. De esta forma, las siguientes sentencias se producen para los mtodos enumerados anteriormente: INSERT: save, persist, saveOrUpdate, replicate. UPDATE: update, merge, saveOrUpdate, replicate. DELETE: delete.
A continuacin se exponen algunos mtodos de la clase Session utilizados habitualmente. Para mayor informacin se puede acceder a la API de Hibernate: http://www.hibernate.org/hib_docs/v3/api/
[60]
HIBERNATE
Comienza una unidad de trabajo y devuelve la instancia de la transaccin asociada. El objeto Transaction es quien tiene los mtodos commit y rollback. Obtiene la instancia de transaccin asociada con esta sesin. El objeto Transaction es quien tiene los mtodos commit y rollback. Crea una nueva instancia de Criteria. Crea una nueva instancia de Query para una cadena HQL. Crea una nueva instancia de SQLQuery para una cadena SQL. Devuelve la instancia persistente de una clase, correspondiente al identificador id, o null si no existe un elemento con dicho identificador. Devuelve la instancia persistente de la clase dada, correspondiente al identificador id, asumiendo que la instancia existe. Este mtodo no debe utilizarse para determinar si existe una instancia (utilizar get en su lugar). Convierte en persistente la instancia actual, asignando en primer lugar un identificador generado (o utilizando el id actual si se ha utilizado un generador asigned). Guarda (save) o actualiza (update) una instancia, dependiendo de si sta era persistente anteriormente o no. Actualiza una instancia persistente con el identificador de la instancia actual. Copia el estado de una instancia en el objeto persistente con el mismo identificador. Si no existe una instancia persistente en la sesin actual la carga. En otro caso, si la instancia no se haba guardado, guarda una copia y la devuelve como una nueva instancia persistente. Convierte una instancia transient en persistente.
public Transaction getTransaction() public Criteria createCriteria(Class pstClass) public Query createQuery(String qryString) public SQLQuery createSQLQuery(String qryString)
[61]
HIBERNATE
Elimina una instancia persistente del almacn de datos. Hace persistente el estado de una instancia, reutilizando su identificador. Vuelve a leer el estado de una instancia de la base de datos. til cuando se ejecutan triggers, se inserta un campo binario, o las sesiones son muy largas. Devuelve el identificador asociado a una instancia. Comprueba si una instancia est asociada con esta sesin. Elimina una instancia de la sesin, con lo cual las actualizaciones no tendrn reflejo en la Base de datos.
4. ASOCIACIONES
Las asociaciones en Hibernate representan las relaciones entre tablas. En un modelo ORM, las claves forneas habitualmente no se modelan con el campo identificador nicamente, sino con instancias completas de objetos para poder aprovechar la potencia del Framework.
[62]
HIBERNATE
En la imagen anterior se puede observar la relacin que existe entre la tabla Person (Persona) y Address (Direccin). Se trata de una relacin uno a muchos donde el campo AddressId acta como una clave fornea de la tabla Address (maestro) en la tabla Person (detalle) lo cual modela que en una Direccin pueden habitar muchas Personas. Este tipo de relacin es muy habitual en Bases de datos y como se ha comentado, se suele denominar uno a muchos (leyendo en la direccin Address Person). En Hibernate suele leerse de forma inversa, muchos a uno (many-to-one), pero sigue tratndose de la misma relacin. Esta relacin a nivel de clases persistentes se modela de forma sencilla a travs de un atributo address en la clase Person, de forma que si se dispone de una instancia de Person se pueden conocer todos los datos de la instancia Address donde reside esa persona, sin necesidad de realizar ningn tipo de consulta adicional a la Base de datos. En una Base de datos irremediablemente habra que realizar una consulta cruzada para recuperar tales datos. El otro atributo fruto de esta relacin que aparece a nivel de clases es el conjunto people. Esto Hibernate se denomina la relacin inversa, ya que las relaciones se pueden modelar en ambos sentidos. Este sentido de la relacin es el que habitualmente se utiliza al realizar una consulta cruzada, es decir, cuando interesa conocer quines son los habitantes de una determinada direccin. De nuevo Hibernate proporciona esta informacin de forma sencilla sin necesidad de implementar consultas, ya que el atributo de tipo Set de la clase Address contendr todas las instancias de la clase Person para aquellas personas que cumplan con la relacin. Para poder aprovechar estas funcionalidades es necesario tener en cuenta otros factores como la configuracin de los ficheros de mapeo, o los mtodos a utilizar para recuperar los valores de la Base de datos. Ambos conceptos se explican ampliamente en este captulo y el siguiente.
[63]
HIBERNATE
Las relaciones que existen entre las tablas dan lugar a modificaciones en las mismas a la hora de normalizar el modelo, de forma que no existan campos duplicados ni prdidas de integridad por un mal diseo.
De las imgenes anteriores cabe destacar un aspecto importante, todas los tipos de relaciones que aparecan en primera instancia, se pueden reducir a un nico tipo, la relacin Muchos a uno, ya que: Las relaciones Uno a Uno se eliminan Las relaciones Muchos a Muchos se transforman en relaciones Muchos a Uno sobre la tabla intermedia.
Este hecho va a simplificar considerablemente la gestin de asociaciones en Hibernate, ya que solamente har falta controlar un tipo de relacin para poder construir modelos ORM que representen modelos Entidad-Relacin de cierta complejidad. Los tipos de asociaciones en Hibernate que servirn para modelar las relaciones citadas es la siguiente: Unidireccionales: solamente se representa un extremo de la relacin. Unidireccionales con tabla de unin. Bidireccionales: se representan ambos extremos. Bidireccionales con tabla de unin.
[64]
HIBERNATE
Teniendo en cuenta esos tipos de relaciones y los dos sentidos de la relacin aparecen en Hibernate 12 tipos distintos de relacin que se pueden modelar de forma directa. Todas estas relaciones se pueden reducir bsicamente a tres tipos que sern explicados a continuacin. Como se ha comentado, lo ms sencillo para el desarrollo puede resultar en el diseo adecuado del modelo Entidad-Relacin de la Base de datos, y simplemente en utilizar mapeos many-to-one para las relaciones entre las tablas. Para mayor informacin se puede consultar la documentacin de Hibernate relativa a Asociaciones: http://www.hibernate.org/hib_docs/v3/reference/en/html/associations.html
4.2. MANY-TO-ONE
La primera de las relaciones que se explicarn es la relacin Many-to-one. Para poder modelar un sistema de Bases de datos puede resultar la nica relacin imprescindible ya que como se ha explicado, el resto de ellas se pueden transformar. Esta relacin normalmente se denomina Maestro-Detalle, donde existe un extremo Maestro que dispone de las claves principales, y un Detalle donde esta clave aparece como fornea. A nivel de Hibernate, la relacin bidireccional se modela de la siguiente forma: Many-to-one en el Detalle. One-to-Many en el Maestro, incorporando el atributo inverse. No es obligatorio modelar este sentido de la relacin. En el siguiente ejemplo aparecen los mapeos que se producirn en las clases para modelar esta relacin. En el apartado 3.3.1 se puede consultar el significado completo de los atributos que aqu aparecen.
[65]
HIBERNATE
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId"/> </class>
En la clase Person se mapea el sentido many-to-one que es el ms sencillo: A la clase se agrega un atributo Address address. En el fichero de mapeo se agrega una etiqueta many-to-one: name: nombre del atributo. column: nombre de la columna de la tabla de base de datos que se corresponde con el atributo (la clave fornea).
<class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true"> <key column="addressId"/> <one-to-many class="Person"/> </set> </class>
En la clase Address se mapea el sentido one-to-many, el sentido inverso: A la clase se agrega un atributo Set people. En el fichero de mapeo se agrega una etiqueta set: name: nombre del atributo. inverse (true): la relacin es inversa. key: column: nombre de la columna de la tabla de base de datos que acta como clave fornea en la otra tabla. one-to-many class: nombre de la clase que modela la otra tabla en la relacin.
[66]
HIBERNATE
4.3.MANY-TO-MANY Como se ha comentado, esta relacin se puede reducir a dos relaciones Many-to-one sobre la tabla intermedia de la Base de datos. En Hibernate se pueden modelar relaciones Many-to-Many con tablas intermedias de forma directa en lugar de recurrir a dos relaciones Many-to-One de la siguiente forma: Many-to-many en cada tabla. No es necesario crear la clase persistente Person_Address.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" class="Address"/> </set> </class>
[67]
HIBERNATE
En la clase Person se mapea el sentido directo: A la clase se agrega un atributo Set addresses. En el fichero de mapeo se agrega una etiqueta set: name: nombre del atributo. table: nombre de la tabla intermedia de la base de datos. key: column: nombre de la columna de la tabla que modela esta clase que acta como clave fornea en la otra tabla. many-to-many class: nombre de la clase que modela la otra tabla en la relacin. column: nombre de la columna de la otra tabla en la relacin que acta como la otra clave fornea en la tabla intermedia.
<class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <set name="people" inverse="true" table="PersonAddress"> <key column="addressId"/> <many-to-many column="personId" class="Person"/> </set> </class>
En la clase Address se mapea el sentido inverso: A la clase se agrega un atributo Set people. En el fichero de mapeo se agrega una etiqueta set: name: nombre del atributo. inverse (true): la relacin es inversa. table: nombre de la tabla intermedia de la base de datos. key: column: nombre de la columna de la tabla que modela esta clase que acta como clave fornea en la otra tabla.
[68]
HIBERNATE
many-to-many class: nombre de la clase que modela la otra tabla en la relacin. column: nombre de la columna de la otra tabla en la relacin que acta como la otra clave fornea en la tabla intermedia.
4.4. ONE-TO-ONE
Como se ha comentado, esta relacin se puede eliminar de la Base de datos pasando los datos de una de las tablas a la otra. En Hibernate se pueden modelar relaciones One-to-One de forma directa de la siguiente forma: Many-to-one en un sentido, incluyendo el atribute unique. One-to-one en el sentido inverso. Se mantienen ambos mapeos.
[69]
HIBERNATE
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class>
En la clase Person se mapea el sentido directo: A la clase se agrega un atributo Address address. En el fichero de mapeo se agrega una etiqueta many-to-one: name: nombre del atributo. column: nombre de la columna de la tabla de base de datos que se corresponde con el atributo (la clave fornea). unique (true): restriccin de unicidad para cumplir la relacin.
<class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class>
En la clase Address se mapea el sentido inverso: A la clase se agrega un atributo Person person. En el fichero de mapeo se agrega una etiqueta one-to-one: name: nombre del atributo. property-ref: nombre de la propiedad que se corresponde con esta clase en el otro extremo.
[70]
HIBERNATE
5. RECUPERACIN DE DATOS
En las bases de datos relacionales, las operaciones ms frecuentes suelen ser las recuperaciones de datos de una o ms tablas o vistas. Para poder realizar esta operacin, en dichas bases de datos se recurre al lenguaje SQL, y ms concretamente a la sentencia SELECT. De esta forma, especificando una sentencia ms o menos compleja es posible obtener la informacin solicitada Como se vio en el apartado anterior, la relacin ms habitual entre dos tablas es la relacin Maestro-Detalle o many-to-one. Sobre esta relacin se pueden efectuar 2 tipos de consultas cruzadas bastante habituales:
SELECT PERSON.NAME, PERSON.AGE, ADDRESS.STREET, ADDRESS.NUMBER FROM PERSON, ADDRESS WHERE PERSON.ADDRESSID = ADDRESS.ADDRESSID
Obtener los nombres de todas las personas que viven en una direccin determinada:
SELECT PERSON.NAME FROM PERSON, ADDRESS WHERE PERSON.ADDRESSID = ADDRESS.ADDRESSID AND ADDRESSID = 1
[71]
HIBERNATE
En Hibernate se podra realizar de la misma forma (SQLQuery) pero lo ms habitual no es esto, sino aprovechar las caractersticas del modelo ORM para acceder a esta informacin de forma ms eficiente. Para ello existen dos alternativas: Usar una consulta HQL para ejecutar la consulta, de forma que usando un lenguaje orientado a objetos pero muy similar a SQL. Usar un Criteria, de forma que a travs de programacin se puedan utilizar diversos mtodos para recuperar los datos deseados de forma similar a como lo hace HQL.
Como se ha venido comentando, la relacin entre las dos tablas anteriores se modela a nivel de clases con una asociacin many-to-one, y los siguientes atributos relacionados: Address address en la clase Person, que permitir acceder a todos los datos de la direccin de una Persona. Set people en la clase Address, que permitir conocer todas las personas que habitan una misma direccin.
Lo que se modela en las clases es exactamente lo que se estaba accediendo a travs de sentencias SELECT anteriores. Por tanto casi todo el trabajo ya est hecho, solamente hace falta acceder a los datos:
En ambos casos se obtiene una lista con una instancia de la clase Person por cada una de las personas de la tabla Person, en cuyo atributo address se puede consultar toda la informacin necesaria sobre la direccin. Sobre esta capacidad es necesario destacar que no siempre Hibernate recupera todos los datos relacionados ya que suele comportarse en modo lazy, es decir, solamente recupera los datos principales de la clase, mientras que los atributos de asociaciones (como address) quedan sin inicializar por motivos de eficiencia. Para forzar que estos atributos siempre se carguen se utilizan los fetch o alias a la hora de recuperar los datos, los cuales se explican con mayor detalle en los apartados posteriores.
[72]
HIBERNATE
Set set = ((Address)session.createQuery("from Address where id=1").list()).getPeople(); Set set = ((Address)session.createCriteria(Address.class). add(Restrictions.eq("id", new Integer(1))) .list()).getPeople();
5.1. HQL
Hibernate usa un potente lenguaje de consulta (HQL), que es similar en apariencia a SQL. En comparacin con SQL, sin embargo, HQL es completamente orientado a objetos y es capaz de gestionar conceptos como herencia, polimorfismo y asociaciones. La informacin completa se puede encontrar en: http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html
Exceptuando los nombres de clases y atributos, el lenguaje HQL es insensible a maysculas (por ejemplo se admite SELECT, select o SEleCT).
Devuelve todas las instancias de la clase Cat. Normalmente NO es necesario utilizar el nombre de paquete (from eg.Cat) ya que la opcin auto-import es true por defecto. A la hora de referirse a Cat desde otras partes de la consulta, normalmente se utiliza un alias a continuacin de la clase. La especificacin del alias puede usar la palabra clave as (as cat) o no: from Cat cat
[73]
HIBERNATE
5.1.3. JOINS
Los joins son la forma de realizar cruces entre tablas en Bases de datos. Habitualmente no se utilizan los joins porque la forma de escribir las consultas resulta ms amigable utilizando condiciones where, de forma que es el gestor de la Base de datos quien se encarga de realizar la transformacin necesaria para utilizar este tipo de unin. Las consultas siguientes son equivalentes en cuanto al resultado obtenido:
SELECT e.nombre, d.nombre FROM empleados e INNER JOIN departamentos d ON d.numero = e.dnumero
Existen varios tipos de JOIN posibles entre 2 tablas de la Base de datos: CROSS JOIN: unin de todos los datos de dos tablas sin especificar ningn criterio (producto cartesiano). Este tipo de JOIN es muy ineficiente.
PROPIETARIOS
PISO 1A 1B 2B NOMBRE Antonio Manuel Javier 10 GARAGE 12
GARAJES
NUMERO 10 11 12 TAMAO 20 22 22
1A 1A 1A 1B 1B
12 12 12
10 11 12 10 11
20 22 22 20 22
[74]
HIBERNATE
INNER JOIN o JOIN: unin de los datos de dos tablas solamente en aquellos casos donde existan datos en el campo de combinacin. Este tipo de JOIN es el que resulta equivalente a realizar la combinacin a travs de un WHERE.
PROPIETARIOS
PISO 1A 1B 2B NOMBRE Antonio Manuel Javier 10 GARAGE 12
GARAJES
NUMERO 10 11 12 TAMAO 20 22 22
PISO 1A 2B
NUMERO 12 10
TAMAO 22 20
En Hibernate tambin se especifica de forma similar a SQL, solamente teniendo en cuenta que no se hacen uniones entre tablas propiamente dichas, sino con los atributos sobre los que se han montado asociaciones. Se puede usar con las palabras clave inner join o inner outer join, o incluso simplemente join:
[75]
HIBERNATE
LEFT OUTER JOIN o LEFT JOIN: se seleccionan todos los datos de la primera tabla de la combinacin, uniendo los datos de la segunda tabla solamente en los casos que sea posible, y poniendo null en los casos que no sea posible.
PROPIETARIOS
PISO 1A 1B 2B NOMBRE Antonio Manuel Javier 10 GARAGE 12
GARAJES
NUMERO 10 11 12 TAMAO 20 22 22
PISO 1A 1B 2B
NUMERO 12 null 10
TAMAO 22 null 20
De nuevo se pueden especificar con HQL sobre los atributos sobre los que se han montado asociaciones uniones de este tipo a travs de left join o left outer join:
from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
RIGHT OUTER JOIN o RIGHT JOIN: se seleccionan todos los datos de la segunda tabla de la combinacin, uniendo los datos de la primera tabla solamente en los casos que sea posible, y poniendo null en los casos que no sea posible.
[76]
HIBERNATE
PROPIETARIOS
PISO 1A 1B 2B NOMBRE Antonio Manuel Javier 10 GARAGE 12
GARAJES
NUMERO 10 11 12 TAMAO 20 22 22
PISO 1A 2B null
NUMERO 12 10 11
TAMAO 22 20 22
Se puede especificar con HQL con right join o right outer join. FULL OUTER JOIN o FULL JOIN: es una combinacin de los dos anteriores.
PROPIETARIOS
PISO 1A 1B 2B NOMBRE Antonio Manuel Javier 10 GARAGE 12
GARAJES
NUMERO 10 11 12 TAMAO 20 22 22
PISO 1A 1B 2B null
NUMERO 12 null 10 11
TAMAO 22 null 20 22
[77]
HIBERNATE
En general, sobre cualquier tipo de join se pueden establecer condiciones: from Cat as cat left join cat.kittens as kitten with kitten.bodyWeight > 10.0
Por ltimo, es muy importante la utilizacin de la palabra clave fetch. Como se ha comentado, normalmente las colecciones o atributos que se refieren a asociaciones mapeadas no se inicializan debido al comportamiento lazy por defecto. Para asegurar la recuperacin de los datos en dichas propiedades, es necesario utilizar fetch en conjuncin con los joins: from Cat as cat inner join fetch cat.mate left join fetch cat.kittens child left join fetch child.kittens
Para realizar un fetch sobre todas las propiedades susceptibles de bsqueda, se puede utilizar una sentencia de este tipo: from Document fetch all properties order by name
Tambin es posible la utilizacin de un join implcito, es decir, no aparece la clusula join en la consulta pero para Hibernate es necesario realizarla para resolver las condiciones impuestas. En este ejemplo no sera necesario realizar un join cat.mate si directamente se incluye como condicin en la condicin where, aunque se recomienda su inclusin por claridad a la hora de leer la consulta: from Cat as cat where cat.mate.name like '%s%'
[78]
HIBERNATE
5.1.4. SELECT
La clusula Select permite obtener propiedades individuales en lugar de recuperar instancias completas. Estas propiedades pueden referirse a atributos simples o a atributos mapeados. En el siguiente ejemplo se accede a la propiedad name (String) y a la propiedad mate (Cat), destacando que en este ltimo caso se hace un join implcito (si no sera necesario incluir un join cat.mate). select cat.name, cat.mate as cmate from Cat cat
Dentro de las clusulas Select se puede hacer referencia al identificador de la clase de dos formas distintas: Utilizando la propiedad id ya que este atributo se encuentra mapeado siempre con ese identificador (cat.id). Utilizando el nombre de la propiedad si sta tiene un nombre distinto (cat.catId).
Cuando se hace una consulta HQL sin Select, a nivel de programacin se recuperan las instancias concretas de la clase implicada en dicha consulta. En el siguiente fragmento de cdigo se recuperan una coleccin de tipo List, donde cada elemento es una instancia de la clase Cat: List list = session.createQuery("from Cat").list(); Cat cat = (Cat) list.get(0);
Cuando se utiliza la clusula Select, o si se realiza un cross join de clases (from Formula, Parameter), resulta imposible para Hibernate determinar qu clase ha de emplear en la recuperacin de los datos, por lo que stos aparecen en forma de Object[]. Por tanto el conjunto de datos resultado sera una coleccin List de Object[]: List list = session.createQuery("select cat.name, cat.mate from Cat cat").list(); Object[] res = (Object[]) list.get(0);
[79]
HIBERNATE
En el Object[] obtenido como resultado, cada una de las posiciones es un objeto de la clase concreta a la que pertenece el atributo, ya sea porque se ha mapeado de forma explcita en el fichero hbm.xml (atributo type de la etiqueta property), o porque Hibernate lo ha resuelto de forma automtica: String name = (String) res[0]; Cat mate = (Cat) res[1];
En lugar de obtener el resultado en un Object[], se puede forzar en la consulta a obtener una coleccin de tipo List: select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
Tambin se puede obtener una coleccin Map, donde los alias actan como claves y los valores son los recuperados en la consulta: select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
Por ltimo, se puede forzar a devolver el resultado en una clase concreta, cuyo constructor debe responder a la estructura utilizada en la consulta: select new Family(mother, mate, offspr) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
[80]
HIBERNATE
En HQL se pueden utilizar las operaciones y expresiones habituales en SQL: Operadores matemticos: +, -, *, / Operadores de comparacin: =, >=, <=, <>, !=, like Operadores lgicos: and, or, not, in, is null, Funciones: concat() (o ||), current_timestamp(), day(), month(),
Asimismo, es posible utilizar funciones agregadas habituales: avg(), sum(), min(), max(), count()
5.1.6. CONDICIONES
La clusula Where se utiliza para la especificacin de condiciones en las consultas. Puede incorporar expresiones para determinar la condicin, de forma similar a como se realiza en SQL, teniendo en cuenta las caractersticas orientadas a objetos de HQL: from Foo foo where foo.bar.baz.customer.address.city is not null
Es posible comparar instancias completas, as como acceder a la propiedad class: from Cat cat, Cat rival where cat.mate = rival.mate and cat.class = DomesticCat
[81]
HIBERNATE
5.1.7. ORDEN
La clusula utilizada para ordenar el conjunto de resultados es Order By: from DomesticCat cat order by cat.name asc, cat.weight desc, cat.birthdate
5.1.8. AGRUPACIN
La agrupacin del conjunto de resultados normalmente es utilizada para la aplicacin de funciones de agregado sobre los grupos. La utilizacin en HQL no difiere de la habitual en SQL: select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY, eg.Color.BLACK)
HQL tambin permite la utilizacin de consultas anidadas tanto en la clusula Select como en las condiciones Where: select cat.id, (select max(kit.weight) from cat.kitten kit) as kitweight from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
[82]
HIBERNATE
5.2.CRITERIA
Hibernate proporciona una API intuitiva y extensible para la construccin de consultas, la cual evita al desarrollador la necesidad de conocer HQL ni SQL para la recuperacin de datos. La informacin completa se puede encontrar en la ayuda de la API de la clase Criteria, o en el siguiente enlace: http://www.hibernate.org/hib_docs/v3/reference/en/html/querycriteria.html
El mtodo createCriteria devuelve un objeto perteneciente al interfaz Criteria. La mayora de los mtodos que se invocan sobre un Criteria devuelven otro objeto de tipo Criteria, de forma que las llamadas se van enlazando para construir una consulta completa. A lo largo de este captulo se explican los distintos mtodos disponibles. List cats = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount() ) .add( Projections.avg("weight") ) .add( Projections.max("weight") ) .add( Projections.min("weight") ) .add( Projections.groupProperty("color") ) ) .addOrder( Order.asc("color") ) .list();
[83]
HIBERNATE
Hay dos formas bsicas de recuperar los datos de un Criteria: Invocando al mtodo list, que devolver una lista con las instancias concretas obtenidas por la consulta. List cats = session.createCriteria(Cat.class) .list(); Cat cat = (Cat) cats.get(0);
Invocando al mtodo uniqueResult, que devolver un nico resultado como consecuencia de la ejecucin de la consulta. Este mtodo resulta en excepcin si el resultado no es un nico registro. Cat cat = (Cat) session.createCriteria(Cat.class) .add(Restrictions.eq("id", new Integer(1))) .uniqueResult();
5.2.2. JOINS
En un Criteria, la nica posibilidad de join que existe es utilizar un fetch: List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "Fritz%") ) .setFetchMode("mate", FetchMode.JOIN) .setFetchMode("kittens", FetchMode.JOIN) .list();
[84]
HIBERNATE
El valor FetchMode.JOIN indica que debe realizarse un inner join a la hora de realizar el fetch. En el caso que se quiera realizar otro tipo de join se utilizar el mtodo createAlias indicando que se trata de un LEFT_JOIN, RIGHT_JOIN o FULL_JOIN: List cats = sess.createCriteria(Cat.class) .createAlias("kittens", "kt", CriteriaSpecification.LEFT_JOIN) .add( Restrictions.eqProperty("kt.name", "miao") ) .list();
En el siguiente ejemplo aparece la creacin de un alias normal, es simplemente equivalente a la creacin de un alias en HQL (as kt): List cats = sess.createCriteria(Cat.class) .createAlias("kittens", "kt") .add( Restrictions.eqProperty("kt.name", "miao") ) .list();
5.2.3. SELECT
La clusula Select de SQL o HQL permite realizar la operacin denominada Proyeccin en lgebra relacional. El mtodo setProjection se encarga de indicar que se van a seleccionar propiedades concretas del conjunto de resultados: List results = session.createCriteria(Domestic.class, "cat") .createAlias("kittens", "kit") .setProjection( Projections.projectionList() .add( Property.forName("cat.name") ) .add( Projections.property("kit.name"), "kitName" ) ) .list();
[85]
HIBERNATE
El mtodo setProjection recibe como parmetro un ProjectionList, que se ir construyendo a travs de mtodos add. El mtodo add recibe la propiedad concreta que se va a proyectar, y el segundo parmetro en caso de existir se trata del alias de dicha propiedad. Para recuperar una propiedad a incluir en la lista de proyecciones se puede utilizar indistintamente Property.forName() o Projections.property(). Al igual que ocurre con HQL cuando se utiliza setProjection resulta imposible para Hibernate determinar qu clase ha de emplear en la recuperacin de los datos, por lo que stos aparecen en forma de Object[].
5.2.4. CONDICIONES
Para establecer condiciones en un Criteria se usa la clase Restrictions. A travs del mtodo add del objeto Criteria se van aadiendo las distintas restricciones, de forma que se van uniendo a travs de una conjuncin, es decir, a travs de and. En la siguiente consulta, la condicin resultante ser name like "Fritz%" and weight >= 10. List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "Fritz%") ) .add( Restrictions.ge("weight", new Integer(10)) ) .list();
Existen restricciones para la mayora de los operadores habituales en consultas SQL. Para unir restricciones a travs de un or se puede usar el mtodo or() para 2 restricciones, o disjunction() para un nmero mayor de restricciones: List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "Fritz%") ) .add( Restrictions.or( Restrictions.eq( "age", new Integer(0) ), Restrictions.isNull("age") )) .list();
[86]
HIBERNATE
List cats = sess.createCriteria(Cat.class) .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) ) .add( Restrictions.disjunction() .add( Restrictions.isNull("age") ) .add( Restrictions.eq("age", new Integer(0) ) ) .add( Restrictions.eq("age", new Integer(1) ) ) .add( Restrictions.eq("age", new Integer(2) ) ) )) .list();
Tambin se pueden obtener restricciones a travs de las propiedades directamente: Property age = Property.forName("age"); List cats = sess.createCriteria(Cat.class) .add( Restrictions.disjunction() .add( age.isNull() ) .add( age.eq( new Integer(0) ) ) .add( age.eq( new Integer(1) ) ) .add( age.eq( new Integer(2) ) ) )) .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) ) .list();
Por ltimo, se pueden establecer restricciones SQL directamente, indicando la condicin SQL, los valores a aplicar y los tipos de dichos valores: List cats = sess.createCriteria(Cat.class) .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) ) .list();
[87]
HIBERNATE
5.2.5. ORDEN
El mtodo utilizado para ordenar el conjunto de resultados es addOrder. Este mtodo recibe una instancia de la clase Order que se puede obtener invocando al mtodo asc() (orden ascendente) o desc (orden descendente) sobre la propia clase Order, o sobre una propiedad directamente: List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "F%") .addOrder( Order.asc("name") ) .addOrder( Property.forName("age").desc() ) .list();
5.2.6. AGRUPACIN
La agrupacin del conjunto de resultados y las funciones de agregado tambin estn permitidas sobre un Criteria. Las funciones agregadas se aaden a la lista de proyecciones del criteria de la misma forma que las propiedades: List results = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount() ) .add( Projections.avg("weight") ) .add( Projections.max("weight") ) .add( Projections.groupProperty("color") ) ) .list();
[88]
HIBERNATE
Para limitar el nmero de elementos devueltos por un criteria se utiliza el mtodo limit(), conjugado con el mtodo setFirstResult() si se quiere empezar en un registro distinto del primero (0). El siguiente Criteria obtiene una lista con 5 instancias de la clase Cat, comenzando en el registro con ndice 2 del conjunto de resultados. List list = sess.createCriteria(Cat.class). setMaxResults(5). setFirstResult(2). list();
5.3.SQL
Tambin se permite la utilizacin de consultas en SQL nativo desde Hibernate, lo cual resulta bastante til en algunos casos como la utilizacin de sentencias DDL o ejecucin de procedimientos. La principal desventaja de este mtodo es que al utilizar el dialecto SQL de una base de datos concreta, la portabilidad de la aplicacin entre distintos tipos de Bases de datos se ve comprometida. La informacin completa se puede encontrar en la pgina correspondiente del portal de Hibernate: http://www.hibernate.org/hib_docs/v3/reference/en/html/querysql.html
[89]
HIBERNATE
Las consultas SQL se especifican a travs del mtodo createSQLQuery: List list = sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS") .list(); Object[] res = (Object[]) list.get(0);
La lista obtenida como resultado estar formada por un Object[] con las instancias que representan a los campos especificados en la consulta. Se pueden especificar los tipos de las instancias devueltas de forma explcita a travs del mtodo addScalar: List list = sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME", Hibernate.STRING) .addScalar("BIRTHDATE", Hibernate.DATE); Object[] res = (Object[]) list.get(0);
Se puede aadir una entidad a una consulta de forma sencilla, para que la lista de resultados sea un conjunto de instancias de la clase concreta: List list = sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS") .addEntity(Cat.class); Cat cat = (Cat) list.get(0);
[90]