Professional Documents
Culture Documents
http://www.hibernar.org/documentacion_es/castellano.html
1 de 198
http://www.hibernar.org/documentacion_es/castellano.html
4.1.1. Implemente un constructor sin argumentos 4.1.2. Porvea una propiedad identificadora (optativo) 4.1.3. Prefiera clases que no sean finales (optativo) 4.1.4. Declare mtodos de acceso y "mutadores" (accessors, mutators) para los campos persistentes. 4.2. Implementar herencia 4.3. Implementar equals() y hashCode() 4.4. Modelos dinmicos 4.5. T-uplizadores 4.6. Extensiones 5. Mapeo O/R bsico 5.1. Declaracin del mapeo 5.1.1. El Doctype o "tipo de documento XML" 5.1.1.1. EntityResolver 5.1.2. hibernate-mapping 5.1.3. class 5.1.4. id 5.1.4.1. Generator 5.1.4.2. El algoritmo hi/lo 5.1.4.3. El argoritmo UUID 5.1.4.4. Columnas de identidad y secuencias 5.1.4.5. Identificadores asignados 5.1.4.6. Claves primarias assignadas por triggers 5.1.5. Generadores de identificador mejorados. 5.1.6. Optimizacin de los generadores de identificador 5.1.7. composite-id 5.1.8. discriminator 5.1.9. version (optativo) 5.1.10. timestamp (optativo) 5.1.11. property 5.1.12. many-to-one 5.1.13. one-to-one 5.1.14. natural-id 5.1.15. component, dynamic-component 5.1.16. properties 5.1.17. subclass 5.1.18. joined-subclass 5.1.19. union-subclass 5.1.20. join 5.1.21. key 5.1.22. elementos column y formula 5.1.23. import 5.1.24. any 5.2. Tipos de Hibernate 5.2.1. Entidades y "value types" 5.2.2. "Value types" bsicos 5.2.3. "Value types" hechos a medida 5.3. Mapear una misma clase ms de una vez 5.4. Identificadores de SQL entrecomillados 5.5. Alternativas de meta-datos 5.5.1. Usar marcadores de XDoclet 5.5.2. Usar anotaciones de JDK 5.0 5.6. Propiedades generadas 5.7. Objetos auxiliares de base de datos 6. Mapeo de Colecciones 6.1. Colecciones persistentes 6.2. Mapeo de colecciones 6.2.1. Claves forneas de las colecciones 6.2.2. Elementos de la coleccin 6.2.3. Colecciones indexadas 6.2.4. Colecciones de valores y asociaciones de-muchos-a-muchos 6.2.5. Asociaciones de-uno-a-muchos 6.3. Mapeos de coleccin avanzados 6.3.1. Colecciones ordenadas
2 de 198
http://www.hibernar.org/documentacion_es/castellano.html
6.3.2. Asociaciones bidireccionales 6.3.3. Asociaciones bidireccionales con colecciones indexadas 6.3.4. Asociaciones ternarias 6.3.5. Usar una <idbag> 6.4. Ejemplos de colecciones 7. Mapeo de asociaciones 7.1. Introduccin 7.2. Asociaciones unidireccionales 7.2.1. de-muchos-a-uno 7.2.2. de-uno-a-uno 7.2.3. de-uno-a-muchos 7.3. Asociaciones unidireccionales con tablas de unin 7.3.1. de-uno-a-muchos 7.3.2. de-muchos-a-uno 7.3.3. de-uno-a-uno 7.3.4. de-muchos-a-muchos 7.4. Asociaciones bidireccionales 7.4.1. de-uno-a-muchos / de-muchos-a-uno 7.4.2. de-uno-a-uno 7.5. Asociaciones bidireccionales con tablas de unin 7.5.1. de-uno-a-muchos / de-muchos-a-uno 7.5.2. de-uno-a-uno 7.5.3. de-muchos-a-muchos 7.6. Mapeos de asociacoones ms complejas 8. Mapeo de componentes 8.1. Objetos dependientes 8.2. Colecciones de objetos dependientes 8.3. Comonentes usados como ndices de un Map 8.4. Componentes usados como identificadores compuestos 8.5. Componentes dinmicos 9. Mapeo de herencia 9.1. Las tres estrategias 9.1.1. Una tabla por jerarqua de clases 9.1.2. Una tabla por subclase 9.1.3. Una tabla por subclase, usando un discriminador 9.1.4. Mezclar "una tabla por jerarqua de clases" con "una tabla por subclase" 9.1.5. Una tabla por cada clase concreta 9.1.6. Una tabla por cada clase concreta, usando polimorfismo implcito 9.1.7. Mezclar polimorfismo implcito con otras estrategias de mapeo de herencia 9.2. Limitaciones 10. Trabajar con objetos 10.1. Estados de un objeto de Hibernate 10.2. Hacer que los objetos se vuelvan persistentes 10.3. Cargar un objeto 10.4. Consultas 10.4.1. Ejecutar consultas 10.4.1.1. Iterar resultados 10.4.1.2. Consultas que devuelven T-uplas 10.4.1.3. Resultados escalares 10.4.1.4. Parmetros vinculados 10.4.1.5. Paginacin 10.4.1.6. Iteracin navegable 10.4.1.7. Externalizar consultas nombradas 10.4.2. Filtrar colecciones 10.4.3. Consultas "Criteria" 10.4.4. Consultas en SQL nativo 10.5. Modificar objetos persistentes 10.6. Modificar objetos desprnendidos 10.7. Deteccin automtica de estado 10.8. Borrar objetos persistentes 10.9. Replicar un objeto entre dos repositorios de datos distintos 10.10. "Flush" de la sesin 10.11. Persistencia transitiva
3 de 198
http://www.hibernar.org/documentacion_es/castellano.html
10.12. Usar metadatos 11. Transacciones y concurrencia 11.1. La sesin y el alcance (scope) de las transacciones 11.1.1. Unidad de trabajo 11.1.2. Conversaciones largas 11.1.3.Considerar la identidad de los objetos 11.1.4. Problemas comunes 11.2. Demarcacin de las transacciones de base de datos 11.2.1. Entornos no administrados 11.2.2. Usar JTA 11.2.3. Manejo de excepciones 11.2.4. Expiracin de transacciones 11.3. Control optimista de concurrencia 11.3.1. Chequeo de versin hecho por la aplicacin 11.3.2. Sesin extendida y versionado automtico 11.3.3. Objetos desprendidos y versionado automtico 11.3.4. Crear un mtodo a medida para el versionado automtico 11.4. "Lock" pesimista 11.5. Modos de liberacin de conecciones 12. Interceptores y eventos 12.1. Interceptores 12.2. Sistema de eventos 12.3. Seguridad declarativa de Hibernate 13. Procesamiento en lotes 13.1. Inserciones en lotes 13.2. Actualizaciones en lotes 13.3. La interfaz StatelessSession 13.4. Operaciones del tipo "Lenguaje de Modificacion de Datos" (DML-style) 14. HQL: El lenguaje de consultas de Hibernate 14.1. Relevancia de maysculas y minsculas 14.2. La clusula "from" 14.3. Asociaciones y "joins" 14.4. Formas de la sintaxis de los "joins" 14.5. Referirse a la propiedad identificadora 14.6. La clusula "select" 14.7. Funciones agregadas 14.8. Consultas polimrficas 14.9. La clusua "where" 14.10. Expresiones 14.11. La clusula "order by" 14.12. La clusula "group by" 14.13. Subconsultas 14.14. Ejemplos de HQL 14.15. Actualizaciones y borrados en masa 14.16. Consejos y trucos 14.17. Componentes 14.18. Sintaxis del "Constructor de Valor de Fila" (row value constructor) 15. Consultas "Criteria" 15.1. Crear una instancia de Criteria 15.2. Acotar el resultado 15.3. Ordenar el resultado 15.4. Asociaciones 15.5. Captura dinmica de asociaciones 15.6. Consultas "Example" 15.7. Proyecciones, agregado y agrupamiento 15.8. Consultas y subconsultas desprendidas 15.9. Consultas por identificador natural 16. SQL nativo 16.1. Usar un SQLQuery 16.1.1. Consultas escalares 16.1.2. Consultas con entidades 16.1.3. Manipular colecciones y asociaciones 16.1.4. Devolver mltiples entidades
4 de 198
http://www.hibernar.org/documentacion_es/castellano.html
16.1.4.1. Alias y referencias a propiedades 16.1.5. Devolver entidades no administradas 16.1.6. Manejar herencia 16.1.7. Parmetros 16.2. Consultas SQL nombradas 16.2.1. Usar return-property para especificar nombres de columna/alias explcitamente 16.2.2. Usar procedimientos almacenados (stored procedures) para efectuar consultas 16.2.2.1. Reglas y limitaciones en el uso de procedimientos almacenados 16.3. SQL a medida para crear, almacenar y borrar 16.4. SQL a medida para cargar 17. Filtrar datos 17.1. Filtros de Hibernate 18. Mapeo XML 18.1. Trabajar con datos XML 18.1.1. Especificar el mapeo XML y el mapeo de la clase al mismo tiempo 18.1.2. Especificar slo un mapeo XML 18.2. Metadatos del mapeo XML 18.3. Manipulacin de los datos XML 19. Mejorar la performance 19.1. Estrategias de captura (fetch) 19.1.1. Trabajar con asociaciones haraganas 19.1.2. Ajustar las estrategias de captura 19.1.3. Proxies de las asociaciones de un solo extremo 19.1.4. Inicializar colecciones y proxies 19.1.5. Usar la captura por lotes 19.1.6. Usar la captura mediante subselects 19.1.7. Usar la captura de propiedades haragana 19.2. El cach de 2do nivel 19.2.1. Mepeos de cach. 19.2.2. Estrategia de slo lectura 19.2.3. Estrategia de lecto/escritura 19.2.4. Estrategia de lecto/escritura no estricta 19.2.5. Estrategia transaccional 19.2.6. Compatibilidad entre el proveedor de cach y la estrategia de concurrencia 19.3. Admninistrar los cachs 19.4. El cach de consultas (query cache) 19.5. Comprender la performance de las colecciones 19.5.1. Taxonoma 19.5.2. Las lists, maps, idbags y sets son las colecciones ms eficientes de actualizar 19.5.3. Las bags y lists son las colecciones inversas ms eficientes 19.5.4. Borrado en una pasada 19.6. Monitorear la performance 19.6.1. Monitorear una SessionFactory 19.6.2. Mediciones 20. Gua de las herramientas (Toolset) 20.1. Generacin automtica del esquema de base de datos 20.1.1. Retocar el esquema de base de datos 20.1.2. Ejecutar la herramienta 20.1.3. Propiedades 20.1.4. Usar Ant 20.1.5. Actualizaciones incrementales del esquema de base de datos 20.1.6. Utilizar Ant para las actualizaciones incrementales del esquema de base de datos 20.1.7. Validacin del esquema de base de datos 20.1.8. Usar Ant para la validacin del esquema de base de datos 21. Ejemplo: Padre/Hijo 21.1. Nota sobre las colecciones 21.2. de-uno-a-muchos bidireccional 21.3. Ciclo de vida de las propagaciones en cascada 21.4. Propagaciones en cascada y unsaved-value 21.5. Conclusin 22. Ejemplo: La aplicacin Weblog 22.1. Clases persistentes 22.2. Mapeos de Hibernate
5 de 198
http://www.hibernar.org/documentacion_es/castellano.html
22.3. Cdigo Hibernate 23. Ejemplo: Mapeos varios 23.1. Empleador/Empleado 23.2. Autor/Obra 23.3. Cliente/Orden/Producto 23.4. Ejemplos miscelneos de asociacin 23.4.1. Asociacin de-uno-a-uno "con tipo" 23.4.2. Ejemplo de clave compuesta 23.4.3. de-muchos-a-muchos con atributo compartido de clave compuesta 23.4.4. Discriminacin basada en el contenido 23.4.5. Asociaciones en claves alternativas 24. Prcticas recomendadas
Prefacio
Trabajar con software orientado a objetos y bases de datos relacionales, puede ser embarazoso y demandar mucho tiempo, en los entornos corporativos actuales. Hibernate es una herramienta de mapeo objeto/relacional para ambientes Java. El trmino "mapeo objeto/relacional" (ORM por sus siglas en ingls) se refiere a esta tcnica de "mapear" la representacin de los datos desde un modelo de objetos hacia un modelo de datos relacional, con un esquema de base de datos basado en SQL. Hibernate no slo se hace cargo del mapeo de clases Java a las tablas de una base de datos (y de los tipos Java a los tipos de la base de datos), sino que tambin provee utilidades para consulta y captura de datos, y puede reducir considerablemente el tiempo que, de otra manera, habra que invertir con el manejo manual de datos mediante SQL y JDBC. La meta de Hibernate es aliviar al programador del 95% de las tareas ms comunes relacionadas con persistencia. Probablemente, Hibernate no sea la mejor solucin para aplicaciones data-cntricas que tengan casi toda su lgica de negocios en procedimientos almacenados (stored procedures) en la base de datos; es ms til con modelos orientados a objetos cuya lgica de negocio reside en la capa intermedia. Sin embargo, Hibernate puede ayudarlo a encapsular o eliminar cdigo SQL que sea especfico de un proveedor de BD, y ayudar en la tarea usual de traducir desde una representacin tabular a un grfico de objetos. Si usted es nuevo en Hibernate y en lo que respecta al Mapeo objeto/relacional, o incluso nuevo en Java, por favor siga los siguientes pasos: Lea el siguiente instructivo: Captulo 1, Introduccin a Hibernate, el cual es una especie de manual con instrucciones paso a paso. El cdigo fuente del instructivo est incluido en la distribucin descargable, en el directorio doc/reference/tutorial/ Lea Captulo 2, Arquitectura para entender en qu entornos Hibernate puede ser usado. chele un vistazo al directorio eg/ en la distribucin de Hibernate. Contiene una simple aplicacin autosuficiente. Copie su driver de JDBC al directorio lib/ y edite etc/hibernate.properties, especificando valores correctos para su base de datos. Desde la consola, situado en el directorio de distribucin, tipee ant eg (usando Ant), o desde Windows, tipee build eg. Use esta documentacin de referencia como su fuente primaria de informacin. Considere leer Java Persistence with Hibernate (http://www.manning.com/bauer2) si necesita ms ayuda con el diseo de aplicaciones o si prefiere un instructivo paso a paso. Tambin visite http://caveatemptor.hibernate.org y descargue la aplicacin de ejemplo para Persistencia de Java con Hibernate. Las preguntas frecuentes (FAQ, por sus siglas en ingls), son contestadas en el sitio de web de Hibernate. En el sitio de web de Hibernate hay vnculos a demostraciones de terceros, ejemplos e instructivos. El rea Comunitaria del sitio de web de Hibernate es un buen recurso acerca de patrones de diseo y varias soluciones de integracin (Tomcat, JBoss AS, Struts, EJB, etc). Si tiene preguntas, utilice el foro en el sitio de Hibernate. Tambin proveemos un sistema JIRA de seguimiento de problemas, para reportes de defectos (bugs) y pedidos de mejoras. Si a usted le interesa la programacin de Hibernate, nase a la lista de correo de programacin de Hibernate. Si le interesa traducir este documento, pngase en contacto con nosotros en la lista de correo de programacin.
6 de 198
http://www.hibernar.org/documentacion_es/castellano.html
A travs de JBoss Inc, hay disponible soporte para desarrollo comercial y de produccin, y entrenamiento para Hibernate. (vase http://www.hibernate.org/SupportTraining/). Hibernate es un componente vital del paquete de productos conocido como "Sistema Empresarial JBoss de Middleware" (JEMS, por sus siglas en ingls).
Importante
Este instructivo sobreentiende que el usuario ya tiene conocimiento de Java y de SQL. Si cualquiera de ambos es nuevo para usted, o no se maneja bien ellos, le recomendamos que comience con una buena introduccin a estas tecnologas, antes de adentrarse en Hibernate. A la larga, le ahorrar tiempo y esfuerzo.
Nota
Hay otra aplicacin a modo de ejemplo/instructivo en el directorio del cdigo fuente /tutorials/eg. Dicho ejemplo se basa en lnea de comandos (consola), y como tal no depende de un contenedor de servlets para poder ser ejecutado. El montaje (setup) bsico es el mismo que para las instrucciones a continuacin.
7 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Nota
Bsicamente, aqu estamos describiendo el archivo /tutorials/web/pom.xml. Vea el sitio de Maven para ms informacin.
Consejo
Aunque no es estrictamente necesario, muchos entornos visuales de progrmacin (IDEs) ya cuentan con integracin con Maven para leer estos archivos POM, y automticamente generar el proyecto por usted (lo cual puede ahorrar mucho tiempo y esfuerzo). Luego creamos una clase que representa el evento que queremos almacenar en la base de datos.
Se puede ver que esta clase usa la convencin estndar de JavBeans para nombrar a sus mtodos "setter" y "getter" (escritura y lectura de propiedades, respectivamente). Este diseo es el recomendado - pero no es obligatorio. Hibernate puede aceder a los campos directamente; la ventaja de los mtodos de acceso es proveer mayor solidez a la hora de refinar ("refactoring") el cdigo. El constructor sin argumentos s es obligatorio, para poder instanciar el objeto mediante reflexin. La propiedad identificadora o id contiene un identificador nico para un evento en particular. Todas las clases de entidad persistentes (las hay menos importantes tambin) necesitarn dicho id, si queremos usar a pleno las capacides de Hibernate. De hecho, la mayora de las aplicaciones (especialmente aplicaciones de web), ya necesitan distinguir objetos por id, as que esta caracterstica debera considerarse una ventaja, ms que una limitacin. De todos modos, usualmente no manipulamos directamente la identidad de un objeto, as que el mtodo "setter" del id debera ser privado. Slo Hibernate asigna ids, cuando el objeto es grabado. Hibernate puede acceder a mtodos en cualquier nivel de acceso (protected, public, private, etc) directamente. La opcin de qu nivel de acceso utilizar es suya, segn el diseo de su aplicacin.
8 de 198
http://www.hibernar.org/documentacion_es/castellano.html
El constructor sin argumentos es obligatorio para todas las clases persistentes; Hibernate tiene que crear los objetos para usted utilizando Java Reflection. El constructor puede ser privado; sin embargo, para poder generar "proxies" en tiempo de ejecucin, y para la captura de datos sin la construccin de bytecode, se requiere al menos el nivel de acceso "package" o por defecto. Ponga este archivo de cdigo fuente Java en un directorio llamado src en el directorio de desarrollo, y dentro del paquete correspondiente. El directorio debera verse as:
. +lib <bibliotecas de Hibernate y de terceros> +src +events Event.java
Ntese que la DTD de Hibernate es bastante sofisticada. La puede usar para autocompletar elementos de mapeo y atributos de XML en su indetfaz grfica (IDE). Tambin puede abrir la el archivo de la DTD en su procesador de texto - es la forma ms fcil de tener una visin general de todos los elementos, y de ver los valores por defecto, junto con algunos comentatios. Sepa que Hibernate no buscar la DTD en la red, sino en el classpath. La DTD est incluida en el archivo, hibernate3.jar, as como en el directorio src/ de la distribucin de Hibernate. En los ejemplos sucesivos, omitiremos la declaracin de la DTD, por brevedad. Por supuesto, sta no es optativa. Entre las dos tags hibernate-mapping, incluya un elemento class. Todas las clases de entidad persistente (de nuevo: hay clases dependientes, como veremos luego, que no son entidades de primer nivel) necesitan dicho mapeo, a una tabla en la base de datos SQL.
<hibernate-mapping> <class name="events.Event" table="EVENTS"> </class> </hibernate-mapping>
Hasta el momento, le hemos dicho a Hibernate cmo persistir y cargar un objeto de la clase Event en la tabla EVENTS, cada instancia representando un registro de la tabla. Ahora continuamos con el mapeo del identificador nico a la clave primaria de la tabla. Por aadidura, como no queremos preocuparnos por manipular dicho identificador, configuramos una "estrategia de generacin de identificador" de Hibernate para que use una clave primaria "sustituta" (surrogate key).
<hibernate-mapping> <class name="events.Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="native"/> </id> </class>
9 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</hibernate-mapping>
El elemento id es la declaracuin de la propiedad indentificadora. El atributo name="id" declara el nombre de la propiedad Java - Hibernate usar los mtodos getter y setter para acceder a ella. El atributo "column" le dice a Hibernate qu columna de la tabla EVENTS usamos como clave primaria. El elemento anidado generator especifica la estrategia para la generacin de identificador. En este caso, usamos native, la cual elige la mejor estrategia dependiendo de la BD y dialecto configurados. Hibernate soporta tanto identificadores generados por la base de datos, como globalmente nicos, o asignados por la aplicacin (o generados por cualquier estrategia para la cual usted haya escrito una extensin). Finalmente, incluimos declaraciones para las propiedades persistentes en el archivo de mapeo. Por defecto, ninguna de las propiedades de la clase se considera persistente.
<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>
Igual que con el elemento id, el atributo name del elemento property le dice a Hibernate qu mtodos getter y setter usar. As que, en este caso, Hibernate buscar getDate()/setDate(), as como getTitle()/setTitle(). Por qu el mapeo de la propiedad date incluye el atributo column, pero el title no? A falta del atributo column, Hibernate por defecto usa el nombre de la propiedad como nombre de columna. Esto funciona para title. Pero date es una palabra reservada en la mayora de las base de datoss, as que mejor la mapeamos con un nombre diferente. Otra cosa interesante, es que el mapeo title tambin carece del atributo type. Los tipos que usamos en los archivos de mapeo no son, como es de esperarse, tipos Java. Tampoco son tipos SQL. A estos tipos se los llama Tipos de mapeo de Hibernate, conversores que podemos traducir de tipos Java a SQL y viceversa. De nuevo, Hibernate tratar de determinar la conversin adecuada e incluso el tipo mismo, si el atributo type no se especifica en el mapeo. En algunos casos, esta deteccin automtica (que usa Java Reflection) puede no generar el valor por defecto que usted esperaba o necesita. se es el caso con la propiedad date. Hibernate no puede saber si la propiedad, que es del tipo java.util.Date, debera ser mapeada a una columna timestamp, o a una columna time. En este caso, mantengamos la informacin completa (de da y hora) mapeando la propiedad al conversor timestamp. Este achivo de mapeo debera ser grabado como Event.hbm.xml, justo en el mismo directorio que el archivo Java de la clase Event. El nombre de los archivos de mapeo puede ser arbitrario, pero los sufijos hbm.xml son una convencin en la comunidad de programadores de Hibernate. Ahora la estructura de directorios debera verse as:
. +lib <bibliotecase de Hibernate y de terceros> +src +events Event.java Event.hbm.xml
10 de 198
http://www.hibernar.org/documentacion_es/castellano.html
subdirectorio data/, y arranque HSQL DB nuevamente. Hibernate es la capa de su aplicacin que se conecta con esta base de datos, as que necesita informacin de conexin. Las conexiones se hacen mediante un pool de conexiones JDBC, el cual tambin tenemos que configurar. La distribucin de Hibernate contiene varias herramientas de cdigo abierto (open source) que generan pool de conexiones , pero para este instructivo usaremos el pool que ya viene incorporado en Hibernate. Dse cuenta de que, si quiere usar un pool de conexiones JDBC de mayor calidad (para aplicaciones ya instaladas en produccin) hecho por terceros, deber copiar las bibliotecas que hagan falta en el classpath, y usar propiedades de conexin diferentes. Para la configuracin de Hibernate, podemos usar un simple archvo hibernate.properties, un archivo hibernate.cfg.xml un tanto ms sofisticado, o incluso una configuracin totalmente programtica. La mayora prefiera el archivo de configuracin XML.
<?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> <!-- datos de conexin de la BD --> <property name="connection.driver_class">org.hsqldb.jdbcDriver</property> <property name="connection.url">jdbc:hsqldb:hsql://localhost</property> <property name="connection.username">sa</property> <property name="connection.password"></property> <!-- pool de conexiones JDBC (usamos el que ya viene incorporado) -->
<property name="connection.pool_size">1</property> <!-- dialecto SQL --> <property name="dialect">org.hibernate.dialect.HSQLDialect</property> <!-- habilita el manejo automtio de contexto de sesin por parte de Hibernate --> <property name="current_session_context_class">thread</property> <!-- inhabilita el cach de 2do nivel --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- imprime todo el SQL ejecutado en la salida estndar <property name="show_sql">true</property> <!-- borra y recrea la BD en cada arranque --> <property name="hbm2ddl.auto">create</property> <mapping resource="events/Event.hbm.xml"/> </session-factory> </hibernate-configuration> -->
Fjese en que este archivo de configuracin XML usa una DTD distinta. Configuramos la SessionFactory de Hibernate una fabrica global responsable de una base de datos en particular. Si tiene varias base de datos, use varias configuraciones de <session-factory> por lo comn en otros tantos archivos de configuracin (para un arranque ms fcil). Los primeros 4 elementos property contienen la configuracin necesaria para la conexn JDBC. La propiedad "dialect" especifica la variante de SQL en particular que Hibernate genera. El manejo automtico de sesiones por contextos de persistencia ser muy til, como pronto veremos. La opcin hbm2ddl.auto activa la generacin automtica de esquemas directamente en la BD. Esto por supuesto puede ser desactivado (quitando esta opcin) o ser redirigido a un archivo, con la ayuda de la tarea de Ant SchemaExport. Fnalmente, agregamos el o los archivo de mapeo para las clases persistentes a la configuracin. Copie este archivo en el directorio de fuentes (src), de manera que quede en la raz del classpath. Hibernate busca automticamente un arhivo llamado hibernate.cfg.xml en la raz del classpath, al arrancar.
11 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Ahora vamos a construir (build) esta aplicacin instructiva usando Ant. Deber tener Ant ya instalado - obtngalo de la Pgina de descarga de Ant. Aqu no vamos a discutir cmo instalar Ant. Por favor refirase al Manual de Ant. Despus de haber instalado Ant, podemos crear el archivo de construccin de Ant (build file), que se llamar build.xml, y estar situado directamente en el directorio de desarrollo. Un archivo de construccin Ant bsico se ve as:
<project name="hibernate-tutorial" default="compile"> <property name="sourcedir" value="${basedir}/src"/> <property name="targetdir" value="${basedir}/bin"/> <property name="librarydir" value="${basedir}/lib"/> <path id="libraries"> <fileset dir="${librarydir}"> <include name="*.jar"/> </fileset> </path> <target name="clean"> <delete dir="${targetdir}"/> <mkdir dir="${targetdir}"/> </target> <target name="compile" depends="clean, copy-resources"> <javac srcdir="${sourcedir}" destdir="${targetdir}" classpathref="libraries"/> </target> <target name="copy-resources"> <copy todir="${targetdir}"> <fileset dir="${sourcedir}"> <exclude name="**/*.java"/> </fileset> </copy> </target> </project>
Esto le dice a Ant que agregue todos los archivos del el directorio lib que terminen en .jar al classpath que usemos para la compilacin. Tambin copiar todos los archivos fuente no-Java al directorio de destino (target), por ejemplo, los archivos de configuracin y mapeo.. Si ejecuta Ant ahora, debera obtener la siguiente salida:
C:\hibernateTutorial\>ant Buildfile: build.xml copy-resources: [copy] Copying 2 files to C:\hibernateTutorial\bin compile: [javac] Compiling 1 source file to C:\hibernateTutorial\bin BUILD SUCCESSFUL Total time: 1 second
12 de 198
http://www.hibernar.org/documentacion_es/castellano.html
import org.hibernate.cfg.*; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Cree la SessionFactory para hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Asegrese de loguear la excepcin, dado que puede ser "tragada" System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
Esta clase no slo produce la SessionFactory global, en un inicializador esttico (invocado una sola vez por la JVM cuando la clase se carga), sino que oculta el hecho de que se emplea un singleton esttico. Podra haber estado buscando la SessionFactory en el JNDI de un servidor de aplicaciones, , por ejemplo,. Si usted le da un nombre a la SessionFactory en su archivo de configuracin, Hibernate en realidad intentar vincularla a JNDI tras haber sido creada. Para omitir este cdigo completamente, usted tambin podra usar "despliegue JMX" y dejar que el un contenedor habilitado para JMX instancie y construya un HibernateService en la JNDI. Estas opciones avanzadas se discuten en la documentacin de referencia de Hibernate. Coloque HibernateUtil.java en el directorio de cdigo fuente, en un paquete al lado de events:
. +lib <Hibernate y las bibliotecas de terceros> +src +events Event.java Event.hbm.xml +util HibernateUtil.java hibernate.cfg.xml +data build.xml
Esto debera poder compilarse sin problemas. Finalmente, necesitamos configurar un sistema de logueo (bitcora, logging) - Hibernate usa commons logging y le deja a usted la opcin entre log4j y el logging especfico de Java. La mayora de los programadores prefiere Log4j. Copie log4j.properties de la distribucin de Hibernate (est en el directorio etc/) a su directorio src, al lado de hibernate.cfg.xml. Dele un vistazo a la configuracin de ejemplo, y cambie los valores si desea una salida ms locuaz. Por defecto, slo los mensajes de arranque de Hibernate se muestran en la salida estndar. La parte infraestructural del instructivo ha finalizado. Ahora estamos listos para efectuar verdadero trabajo con Hibernate.
13 de 198
http://www.hibernar.org/documentacion_es/castellano.html
EventManager mgr = new EventManager(); if (args[0].equals("store")) { mgr.createAndStoreEvent("My Event", new Date()); } HibernateUtil.getSessionFactory().close(); } private void createAndStoreEvent(String title, Date theDate) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); session.save(theEvent); session.getTransaction().commit(); } }
Creamos un objeto Event, y se los pasamos a Hibernate. Ahora Hibernate se encarga del SQL, y ejecuta INSERTs en la base de datos. Echmosle un vistazo a la sesin, y al cdigo de manejo de transacciones antes de ejecutarlo. Usa sesin (Session) es una unidad de trabajo. Por ahora mantendremos todo simple y asumiremos una correspondencia uno-a-uno entre una sesin de Hibernate y una transaccin de BD. Para "escudar" nuestro codigo respecto del sistema subyacente de transacciones (en este caso, slo JDBC, pero podra haber sido JTA), usamos la API para transacciones que est disponible en la clase Session. Qu hace sessionFactory.getCurrentSession()? Primero, a este mtodo se lo puede llamar desde dondequiera, y cuantas veces se desee, una vez que obtenemos una SessionFactory. (fcilmente, gracias a la HibernateUtil). El cdigo getCurrentSession() siempre devuelve la unidad "actual" de trabajo. Recuerda que configuramos una opcin con valor "thread" en hibernate.cfg.xml? Debido a esto, la unidad actual de trabajo est ligada al thread de Java que se est ejecutando en ese momento en su aplicacin. De todos modos, sta no es toda la historia: tambin hay que considerar el alcance (scope), cundo una unidad de trabajo empieza y cundo termina. Una sesin comienza cuando se la necesita por primera vez, cuando se hace la primera llamada a getCurrentSession(). Entonces, es ligada al thread actual por Hibernate. Cuando la transaccin termina, Hibernate desliga la sesin del thread, y la cierra por usted. Si usted llama getCurrentSession() de nuevo, obtiene una nueva sesin y comienza una nueva unidad de trabajo. El modo de programacin "ligado a threads" (thread-bound), es la forma ms difundida de usar Hibernate, dado que permite una distribucin en capas muy flexible: el cdigo de delimitacin de transacciones puede separarse del cdigo de acceso a datos, como veremos ms adelante. En relacin al alcance de la unidad de trabajo: Una sesin debera usarse para ejecutar una sola operacin de base de datos, o varias? El ejemplo precedente usa una sesin para una operacin. Esto es simple casualidad, el ejemplo no es lo suficientemente complejo como para demostrar ningn otro enfoque. El alcance de una sesin de Hibernate es flexible, pero nunca se debe designar una aplicacin de maneera que utilice una sesin para cada operacin de base de datos. As que, incluso si usted lo ve en algunos pocos de los ejemplos siguientes, considere la prctica de "una sesin por operacin" como algo a evitar (un "anti-pattern"). Una aplicacin real (de web) se analiza ms adelante en este instructivo. chele un vistazo al captulo Captulo 11, Transacciones y Concurrencia acerca del manejo de transacciones y su delimitacin. Tambin hemos salteado cualquier manejo de errores en el ejemplo precedente. Para ejecutar esta primera rutina, tenemos que agregar una "target" invocable al archivo de construccin de Ant.
<target name="run" depends="compile"> <java fork="true" classname="events.EventManager" classpathref="libraries"> <classpath path="${targetdir}"/> <arg value="${action}"/> </java> </target>
El valor del argumento "action" se asigna en la lnea de comandos cuando esta target se invoca.
14 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Despus de la compilacin, usted debera ver que que Hibernate arracna, y, dependiendo de su configuracin, un montn de salida de logueo. Al final encontrar la siguiente lnea:
[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)
se es el cdigo INSERT ejecutado por Hibernate. Los signos de interrogacin representan parmetros JDBC ligados. Para ver los valores de dichos parmetros, o para reducir la locuacidad del archivo de log, revise su archivo log4j.properties. Ahora, querramos tanbin listar los eventos almacenados, as que agregamos una opcin el el mtodo principal:
if (args[0].equals("store")) { mgr.createAndStoreEvent("My Event", new Date()); } else if (args[0].equals("list")) { List events = mgr.listEvents(); for (int i = 0; i < events.size(); i++) { Event theEvent = (Event) events.get(i); System.out.println("Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()); } }
Lo que hicimos aqu, es usar el lenguaje de consultas de Hibernate (HQL, por sus siglas en ingls) para cargar todos los objetos Event que existen en la base de datos. Hibernate generar el cdigo SQL que haga falta, lo enviar la base de datos, y poblar los objetos Event con los datos que sean devueltos. Se pueden crear consultas SQL mucho ms complejas con HQL, por supuesto. Ahora, para ejecutar y chequear todo esto, siga estos pasos: Ejecute ant run -Daction=store para almacenar algo en la base de datos y, por supuesto, para previamente generar el esquema de base de datos mediante hbm2ddl. Ahora inhabilite hbm2ddl (convirtiendo la propiedad en un comentario) en el archivo hibernate.cfg.xml. Por lo general, slo se la deja habilitada cuando se efecta un "unit testing continuo", pero en este caso, dejarla habilitada borrara todo lo que usted haya almacenado hasta ese momento. (el valor de hbm2ddl="create" se traduce como "haga un DROP de todas las tablas del esquema, y recree todas las tablas cuando la SessionFactory sea construida") Si usted ahora invocara Ant con -Daction=list, debera ver los eventos que haya almacenado hasta ese moento. Por supuesto, puede tambin invocar la accin store un par de veces ms. Nota: A esta altura, la mayora de los usuarios de Hibernate experimenta problemas, y aparecen seguido mensajes del tipo Table not found . De todos modos, si usted sigue los pasos que acabamos de describir cuidadosamente, no tendr este problema, ya que hbm2ddl crea el esquema de base de datos la primera vez, y las veces subsiguientes en que la aplicacin recomienza utilizan dicho esquema. Si usted en algn momento cambia algo del mapeo o del esquema, debe rehabilitar hbm2dll nuevamente para recrear la BD.
15 de 198
http://www.hibernar.org/documentacion_es/castellano.html
clase. Primero, agregaremos algunas personas a nuestra aplicacin, y almacenaremos una lista de eventos en los cuales participan.
public Person() {} // mtodos "getter" y "setter" de acceso, y setter privado para 'id' }
Cree un nuevo archivo de mapeo llamado Person.hbm.xml (y no olvide la referencia a la DTD en la parte superior):
<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>
Y ahora crearemos una asociacin entre estas dos entidades, Obviamente, las personas pueden participar en eventos, y los eventos tienen participantes. Las cuestiones de diseo con las que tenemos que lidiar son: multiplicidad, direccionalidad, y comportamiento de las colecciones.
16 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Antes de mapear esta asociacin, piense en el otro lado (los eventos). Claramente, podemos simplemente mantener esta asociacin unidireccional. O si no, podramos crear otra coleccin en la clase Event, si deseamos navegar en forma bidireccional, es decir, anEvent.getParticipants(). Esto no es estrictamente necesario, desde un punto de vista funcional, siempre se puede ejecutar una consulta explcita para obtener los participamntes de un determinado evento. Esta es una opcin de diseo que le dejamos a usted; pero lo que sacamos en limpio de esta discusin, es la multiplicidad de la asociacin: hay "muchos valores" desde ambos lados. A esto se lo llama una asociacin "de-muchos-a-muchos" (many-to-many). Por esto, usamos el mapeo many-to-many de Hibernate.
<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"/> <set name="events" table="PERSON_EVENT"> <key column="PERSON_ID"/> <many-to-many column="EVENT_ID" class="events.Event"/> </set> </class>
Hibernate soporta todo tipo de mapeos de coleccn, siendo el "set" el ms comn. Para una asociacin "many-to-many", o relacin entre entidades, se necesita una tabla de asociacin. Cada registro de esta tabla simboliza un vnculo entre una persona y un evento. El nombre de esta tabla se configura con el atributo table del elemento set. La columna indentificadora por el lado de las personas, se define con el elemento <key>, el nombre de la columna por el lado de los eventos, con el atributo column del <many-to-many>. Usted tambin debe decirle a Hibernate la clase de objetos de su coleccin. El esquema de base de datos para este esquema es, entonces:
_____________ __________________ | | | | _____________ | EVENTS | | PERSON_EVENT | | | |_____________| |__________________| | PERSON | | | | | |_____________| | *EVENT_ID | <--> | *EVENT_ID | | | | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | | TITLE | |__________________| | AGE | |_____________| | FIRSTNAME | | LASTNAME | |_____________|
Despus de cargar una Person y un Event, simplemente modifique la coleccin usando los mtodos normales de las colecciones. Como puede usted ver, no hay llamados explcitos a update() o a save(), Hibernate detecta automticamente que la coleccin ha sido modificada y necesita ser actualizada. Esto se llama "dirty checking automtico", y usted puede experimentar con ello cmabiando la propiedad "date" en cualquiera de sus objetos. Siempre y cuando se mantengan en un estado persistente (es decir, ligados a una sesin de Hibernate, por haber sido cargados o
17 de 198
http://www.hibernar.org/documentacion_es/castellano.html
grabados en una unidad de trabajo), Hibernate monitorear cualquier cambio y ejecutar SQL entre bambalinas. El proceso de sincronizar el estado de lo que est en memoria con la base de datos, generalmente al final de la unidad de trabajo, se denomina "nivelar, desagotar" la sesin (en ingls, flush). En nuestro cdigo, la unidad de trabajo termina con un "commit" (o "rollback") de la transaccin de base de datos, tal como se define por la opcin de configuracin thread para la clase CurrentSessionContext. Se podra, por supuesto, cargar personas y eventos en distintas unidades de trabajo. O modificar un objeto fuera de una sesin, cuando no est en estado persistente (si lo estuvo anteriormente, este estado se llama "desprendido", en ingls detached). Incluso una coleccin se puede modificar estando en este estado desprendido.
private void addPersonToEvent(Long personId, Long eventId) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session .createQuery("select p from Person p left join fetch p.events where p.id = :pid") .setParameter("pid", personId) .uniqueResult(); // Eager fetch the collection so we can use it detached Event anEvent = (Event) session.load(Event.class, eventId); session.getTransaction().commit(); // fin de la primera unidad de trabajo aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached // comienzo de la segunda unidad de trabajo Session session2 = HibernateUtil.getSessionFactory().getCurrentSession(); session2.beginTransaction(); session2.update(aPerson); // reasociacin de aPerson, que estaba desprendida session2.getTransaction().commit(); }
El llamado a update convierte de nuevo en persistente un objeto que estaba desprendido. Se podra decir que lo liga a una nueva unidad de trabajo, de manera que cualquier modificacin que se le hubiera hecho mientras estaba desprendido pueda ser grabada en la base de datos. Esto incluye cualquier modificacin que se le hubiera hecho a la coleccin de ese objeto entidad. Bueno, esto no es muy til en la situacin actual, pero es un concepto importante, que usted puede aplicar al diseo de su propia aplicacin. Por ahora, simplemente complete este ejercicio agregando una nueva accin al mtodo main() de EventManager, e invquelo desde la lnea de comandos. Si necesita los identificadores de una persona y de un evento, el mtodo save() los devuelve (tal vez usted deba modificar alguno de los mtodos previos, para que devuelvan dicho identificador).
else if (args[0].equals("addpersontoevent")) { Long eventId = mgr.createAndStoreEvent("My Event", new Date()); Long personId = mgr.createAndStorePerson("Foo", "Bar"); mgr.addPersonToEvent(personId, eventId); System.out.println("Added person " + personId + " to event " + eventId); }
ste fue un ejemplo de una asociacin entre dos clases igualmente importantes, dos "entidades". Como se mencion anteriormente, hay otras clases y tipos "menos importantes" en un modelo tpico. Algunos, usted ya los ha visto, como los int o String. A esas clases las llamamos value types, y sus instancias dependen de una entidad en particular. Las instancias de estos tipos no tienen su propia identidad, ni se pueden compartir entre entidades (dos personas no pueden, por ejemplo, hacer referencia al mismo objeto firstname o primer nombre, incluso si son tocayos). Por supuesto, estos value types pueden ser encontrados no solamente en la JDK. De hecho, en una aplicacin Hibernate todas las clases JDK son consideradas "value types", pero usted puede escribir sus propias clases dependientes, para representar una direccin o una cantidad de dinero, por ejemplo. Tambin se puede disear una coleccin de "value types". Esto es conceptualmente bien distinto de una coleccin de referencias a otras entidades, pero en el cdigo Java ambas se ven casi igual.
18 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Agreguemos una coleccin de objetos "value type" a la entidad Person. Queremos almacenar direcciones de correo electrnico (email), de manera que el tipo que usaremos es String, y la coleccin, de nuevo un Set.
private Set emailAddresses = new HashSet(); public Set getEmailAddresses() { return emailAddresses; } public void setEmailAddresses(Set emailAddresses) { this.emailAddresses = emailAddresses; }
La diferencia, comparada con el mapeo anterior, es la parte element, la cual le dice a Hibernate que la coleccin no contiene referencias a otra entidad, sino a una coleccin de elementos de tipo string (al estar con minscula nos damos cuenta de que es un tipo/conversor de Hibernate). Une vez ms, el atributo table del set determina el nombre de la tabla para la coleccin. El elemento key define el nombre de columna para la clave fornea, El atributo column de "element" define en dnde estas cadenas van a ser almacenadas. Echmosle un vistazo al nuevo esquema de DB
_____________ __________________ | | | | _____________ | EVENTS | | PERSON_EVENT | | | ___________________ |_____________| |__________________| | PERSON | | | | | | | |_____________| | PERSON_EMAIL_ADDR | | *EVENT_ID | <--> | *EVENT_ID | | | |___________________| | EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID | <--> | *PERSON_ID | | TITLE | |__________________| | AGE | | *EMAIL_ADDR | |_____________| | FIRSTNAME | |___________________| | LASTNAME | |_____________|
Se puede ver que la clave primaria de la tabla de la coleccin es, en realidad, una clave compuesta, que usa ambas columnas. Esto tambin implica que no puede haber direcciones de email duplicadas para una misma persona, lo cual es precisamente la semntica de un conjunto o "Set" en Java. Ahora podemos intentar agregar elementos a esta coleccin, igual que como hicimos antes al vincular personas y eventos. El cdigo Java es el mismo:
private void addEmailToPerson(Long personId, String emailAddress) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Person aPerson = (Person) session.load(Person.class, personId); // el getEmailAddresses() podra disparar una carga "haragana" de la coleccin aPerson.getEmailAddresses().add(emailAddress); session.getTransaction().commit(); }
Esta vez, no utilizamos una consulta con fetch para inicializar la coleccin. De ah que la invocacn de su mtodo "getter" disparar un SELECT adicional para inicializarla, de manera que podamos agregarle un elemento. Monitoree el log de SQL e intente optimizar esto con un "eager fetch" (captura ansiosa).
19 de 198
http://www.hibernar.org/documentacion_es/castellano.html
ambos lados en Java. Desde luego, el esquema de base de datos no cambia, todava tenemos una multiplicidad de-muchosa-muchos (many-to-many). Una base de datos relacional es ms flexible que un lenguaje de programacin, no necesita cosas como una "direccin de navegacin", los datos pueden ser adquiridos de cualquier manera posible. Primero, agregue una coleccin de participantes al cdigo de la clase Event.
private Set participants = new HashSet(); public Set getParticipants() { return participants; } public void setParticipants(Set participants) { this.participants = participants; }
Como puede ver, stos son mapeos set normales en ambos documentos de mapeo. Fjese en que los nombres de columna en ambos documentos han sido alternados. La adicin ms importante es el atributo inverse="true" en el elemento set del mapeo de la coleccin de Event. Lo que esto significa, es que Hibernate debera rferirse al otro lado (el lado "Person"), cuando necesite obtener informacin sobre el vnculo entre los dos. Esto ser ms fcil de entender una vez que veamos cmo se crea el vnculo bidireccional entre las dos entidades.
Fjese en que los mtodos "get" y "et" de la coleccin ahora son "protected" - esto les permite a las clases en el mismo paquete, y a las subclases, acceder a los mtodos, pero les impide a todos los dems inmiscuirse con estas colecciones directamente (bueno... casi). Usted debera, probablemente, hacer lo mismo con las colecciones del otro lado. Y qu hay del atributo de mapeo inverse? Para usted (y para Java), un vnculo bidrecciolal es simplemente cuestin de asignar correctamente las referencias en ambos lados .Hibernate, sin embargo, no tiene informacin suficiente como para organizar adecuadamente sus llamados a comandos SQL INSERT y UPDATE. Marcar uno de los lados de la asociacin como inverse, bsicamente le dice a Hibernate que lo ignore, que lo considere como un espejo de lo que ocurre en el otro
20 de 198
http://www.hibernar.org/documentacion_es/castellano.html
lado. Eso es todo lo que se necesita para que Hibernate resuelva todos los problemas que derivan de transformar un modelo de navegacin bidireccional en un esquema de base de datos. Las reglas que usted denbe recordar son son simples: Toda asociacin bidireccional necesita que uno de sus lados sea "inverse". En una asociacin de-uno-a-muchos (one-to-many) tiene que ser el lado "many". En una asociacin de-muchos-a-muchos (many to many), elija cualquier lado, da lo mismo.
Este servlet maneja requests HTTP del tipo GET solamente, as que el mtodo que implementamos es doGet():
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy"); try { // comienzo nuestra unidad de trabajo HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction(); // procesamiento de la solicitud (request) y presentacin de la pgina... // fin de la unidad de trabajo HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit(); } catch (Exception ex) { HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback(); throw new ServletException(ex); } }
El "pattern" o patrn de programacn que estamos aplicando aqu se llama session-per-request, (una sesin por cada "solicitud" o request HTTP). Cuando una nueva request apela al servlet, se abre una nueva sesin de Hibernate mediante el primer llamado a getCurrentSession() de la SessionFactory. Entonces, ss inicia una transaccin de base de datos (todo el cdigo de acceso a datos ocurre dentro de la transaccin, no importa si para lectura o escritura; en las aplicaciones no usamos auto-commit). Nunca cree una nueva sesin de Hibernate para cada operacin de base de datos. Use una sesin de Hibernate a lo largo de toda la request, es decir, que tenga un "alcance" de toda la sesin. Use getCurrentSession(), de manera que quede automticamente ligada al Thread actual. A continuacin, las acciones posibles de la request son procesadas, y es generada la respuesta HTML. Enseguida nos dedicaremos a esa parte. Finalmente, la "unidad de trabajo" finaliza, cuando el procesamiento y presentacin hayan sido completados. Si ocurri algn problema durante este procesamiento o presentacin, se lanzar una excepcin y la se ejecutar un "rollback" de la
21 de 198
http://www.hibernar.org/documentacion_es/castellano.html
transaccin. Esto cubre el "pattern" session-per-request. En lugar de escribir cdigo para demarcar la transaccin en cada request, usted debera crea un Servlet Filter. Refirase al sitio de web de Hibernate y a la Wiki para ms informacin acerca de este "pattern", llamado Open Session in View u OSIV, algo as como "apertura de una sesin por cada vista". Este patrn se necesitar en cuanto usted considere presentar sus "vistas" usando JSP en lugar de servlets.
Concedido, este tipo de escritura de cdigo, mezclando Java y HTML, sera inadmisible en aplicaciones ms complejas, tenga en cuenta que slo estamos ilustrando conceptos bsicos de Hibernate en este instructivo. El cdigo imprime un encabezado y un pie de pgina en HTML. Dentro de la pgina propiamente dicha, se imprime un formulario (form) para el ingreso de eventos. El primer mtodo es trivial, y slo produce HTML:
private void printEventForm(PrintWriter out) { out.println("<h2>Add new event:</h2>"); out.println("<form>"); out.println("Title: <input name='eventTitle' length='50'/><br/>"); out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>"); out.println("<input type='submit' name='action' value='store'/>"); out.println("</form>"); }
El mtodo listEvents() usa la sesin de Hibernate ligada al Thread actual para ejecutar una consulta:
private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) { List result = HibernateUtil.getSessionFactory().getCurrentSession().createCriteria(Event.class) if (result.size() > 0) { out.println("<h2>Events in database:</h2>"); out.println("<table border='1'>"); out.println("<tr>"); out.println("<th>Event title</th>"); out.println("<th>Event date</th>"); out.println("</tr>"); for (Iterator it = result.iterator(); it.hasNext();) { Event event = (Event) it.next(); out.println("<tr>"); out.println("<td>" + event.getTitle() + "</td>"); out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>"); out.println("</tr>"); } out.println("</table>"); }
22 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Finalmente, la accin store es despachada al mtodo createAndStoreEvent(), el cual usa la sesin del Thread actual.
protected void createAndStoreEvent(String title, Date theDate) { Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); HibernateUtil.getSessionFactory().getCurrentSession().save(theEvent); }
Eso es todo. El servlet est completo. Una request al servlet ser procesada en una nica sesin y transaccin. Tal como ocurri en el ejemplo anterior, Hibernate puede ligar estos objetos al Thread actual de ejecucin. Esto le da a usted la libertad de acceder a la SessionFactory de cualquier manera que guste. Normalmente, usted usara un diseo ms sofisticado, y movera el cdigo de acceso a datos (DAO, por sus siglas en ingls) a otra "capa". Refirase a la Wiki de Hibernate para ms ejemplos.
Esta target crea un archivo llamado hibernate-tutorial.war en su directorio de proyecto. Empaqueta todas las bibliotecas y el descriptor web.xml, el cual se espera que est en el directorio base de su proyecto.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4 <servlet> <servlet-name>Event Manager</servlet-name> <servlet-class>events.EventManagerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Event Manager</servlet-name> <url-pattern>/eventmanager</url-pattern> </servlet-mapping> </web-app>
Antes de compilar y desplegar, como sta es una aplicacin de web, note que se necesita una biblioteca adicional : jsdk.jar. Es el kit de herramientas o "developer kit" para los servlets de Java. Si an no tiene esta biblioteca, descrguela del sitio de web de Sun y colquela en su directorio de bibliotecas (lib). De todos modos, se la usa para compilar solamente, no se incluye en los paquetes WAR. Para construir (build) y desplegar (deploy), invoque el comando ant war situado en el directorio de su proyecto, y luego copie el archivo hibernate-tutorial.war en el directorio webapp de Tomcat. Si no tiene Tomcat instalado, descrguelo y siga las instrucciones. No es necesario que cambie nada en la configuracin de Tomcat para que este ejemplo ande. Una vez desplegada y con Tomcat andando, accceda a la aplicacin en http://localhost:8080/hibernate-tutorial /eventmanager. Asegrese de monitorear el log de Tomcar para comprobar si Hibernate se inicializa cuando ocurre la primera solicitud (request) a su servlet (tiene que ejecutarse el inicializado esttico en HibernateUtil), y tambin asegrese de obtener una mensaje detallado si ocurre algn error.
23 de 198
http://www.hibernar.org/documentacion_es/castellano.html
1.5. Sumario
Este instructivo cubri lo bsico para escribir una simple aplicacin Hibernate autosuficiente, y una pequea aplicacin de Web. Si ya va tomando confianza con Hibernate, contine hojeando la tabla de contenidos en la documentacin de referencia, buscando temas que le interesen. Los ms solicitados son el procesamiento de transacciones (Captulo 11, Transacciones y Concurrencia), la performance de las capturas de datos o "fetch performance" (Captulo 19, Mejorar la performance), el uso de las API (interfaces de programacin) (Captulo 10, Trabajar con objetos) y las caractersticas de las consultas (Seccin 10.4, Consultar). No olvide chequear el sitio de web de Hibernate en busca de ms instructivos especializados.
Este diagrama muestra a Hibernate usando datos de configuracin y base de datos para proveerle servicios de persistencia (y objetos persistentes) a la aplicacin. Nos gustara mostrar una vista ms detallada de la arquitectura en tiempo de ejecucin. Lamentablemente, Hibernate es tan flexible, que soporta muchas estrategias. Vamos a mostrar los dos extremos: la arquitectura "liviana" fuerza a la aplicacin a proveer sus propias conexiones JDBC y a gerenciar sus propias tranascciones. Esta arquitectura usa un subconjunto mnimo de las APIs de Hibernate.
24 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La arquitectura "con todas las luces" abstrae la aplicacin, alejndola de las capas subyacentes de JDBC/JTA, y deja que Hibernate se encargue de los detalles.
He aqu algunas definiciones de los objetos en los diagramas: SessionFactory (org.hibernate.SessionFactory) Un cach thread-safe e inmutable de mapeos, compilados para una base de datos en particular. Una fbrica (factory) de sesiones, y cliente de ConnectionProvider. Puede contener un cach optativo (llamado "de 2do nivel") que es reusable entre transacciones, a nivel de proceso o de cluster. Session (org.hibernate.Session) Un objeto de thread simple y de corta visa, que representa una conversacin entre la aplicacin y el repositorio persistente. Envuelve a una fbrica de conexiones JDBC por transaccin. Contiene un cach obligatorio (llamado "de primer nivel") de objetos persistentes, que se usa al navegar el rbol de objetos, o cuando se buscan los objetos por identificador. Objetos y colecciones persistentes Objetos de un solo thread y corta vida, que contienen "estado" persistente y cumplen una funcin de negocio. Pueden ser JavaBeans comunes, o puros y simples objetos de Java (POJOs por sus siglas en ingls), la nica caracterstica notable que tienen, es que estn asociados con una sesin. En cuanto la sesin se cierra, sern desprendidos, y quedarn listos para ser usados en cualquier capa de la aplicacin (por ejemplo, directamente como objetos de transmisin de datos o "DTOs", desde y hacia la capa de presentacin).
25 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Objetos y colecciones transitorios y desprendidos Instancias de clases persistenteses que, por el momento, no estn asociadas con una sesin. Pudieron haber sido instanciadas por la aplicacn, y an no haber sido asociadas con una sesin, o bien haber sido instanciadas por una sesin que en ese momento est cerrada. Transaccin (org.hibernate.Transaction) (Optativo) un objeto de un solo thread y corta vida, usado por la aplicacon para especificar unidades atmicas de trabajo. Abstrae a la aplicacin de la transaccin JDBC, JTA o CORBA subyacente. Una sesn puede extenderse a lo largo de varias transacciones en algunos casos. Pero, sea como sea, el cdigo para demarcar transacciones (ya sea utilizando APIs subyancentes o la interfaz Transaction) nunca es optativo! ConnectionProvider (org.hibernate.connection.ConnectionProvider) (Optativo) Una fbrica y repositorio (pool) de conexiones JDBC. Abstrae la aplicacin de la fuente de datos (Datasource) o del gerente de driver (DriverManager) subyacentes. No est expuesto directamente a la aplicacin, pero puede ser implementado o extendido por el programador. TransactionFactory (org.hibernate.TransactionFactory) (Optativo) Una fbrica de instancias de Transaction. No est expuesta directamente a la aplicacin, pero puede ser implementada o extendida por el programador. Interfaces de extensin Hibernate ofrece varias interfaces de extensin optativoes, se pueden implementar para personalizar el comportamiento de la capa de persistencia. Vea la documentacin de la API para ms detalles. En la arquitectura "liviana", la aplicacin se saltea las APIs de Transaction/TransactionFactory y/o ConnectionProvider APIs para dialogar con JTA o JDBC directamente.
26 de 198
http://www.hibernar.org/documentacion_es/castellano.html
despliega (deploy) usando JMX: Manejo de sesiones: El ciclo de vida de una sesin de Hibernate puede ser automticamente ligado al alcance (scope) de una transaccin JTA. Esto significa que usted ya no necesita abrir y cerrar manualmente las sesiones, de esto se encarga el interceptor de JBoss. Tampoco debe preocuparse ya por demarcar la transaccin en su cdigo (a menos que quiera escribir usted mismo una capa de persistencia porttil, use la API Transaction de Hibernate para eso). Puede llamar a HibernateContext para acceder a una sesin. Despliegue del Archivo de Hibernate (HAR por sus siglas en ingls): Normalmente se despliega el servicio JMX de Hibernate usando el "descriptor de despliegue" (deployment descriptor), en un archivo EAR o SAR, el cual soporta las opciones de configuracin de una SessionFactory de Hibernate. De todos modos, para esto an se debe nombrar a todos los archivos de mapeo en el descriptor de despliegue. En cambio, si decide usar el despliegue HAR, JBoss detecta automticamente todos los archivos de mapeo en su archivo HAR. Consulte la gua del usuario del servidor de aplicaciones JBoss para mayor informacin acerca de estas opciones. Otra caracterstica disponible en forma de servicio JMX son las estadsticas de Hibernate en tiempo de ejecucin. Vea la Seccin 3.4.6, Estadsticas de Hibernate
- las sesiones actuales son localizadas por thread de ejecucin. Sin embargo, usted es responsable por ligar y desligar las instancias de sesin, mediante mtodos estticos en esta clase. Nunca abre, cierra o le aplica flush a una sesin.
Las dos primeras implementaciones proveen un modelo de programacin del tipo "una sesin - una transaccin de base de
27 de 198
http://www.hibernar.org/documentacion_es/castellano.html
datos", tambin llamado session-per-request. El comienzo y fin de una sesin de Hibernate est definido por la duracin de la transaccin de base de datos. Si usted usa demarcacin programtica de transacciones, en simple JSE sin JTA, se le aconseja que use la API de Transaction de Hibernate, para ocultar el sistema de transacciones subyacente. Si el cdigo se est ejecutando en un contenedor EJB que soporte CMT, los lmites de la transaccin son definidos declarativamente, y usted no necesita ninguna transaccin ni demarcacin de operaciones en el cdigo propiamente dicho. Refirase al Captulo 11, Transacciones y Concurrencia para ms informacin y ejemplos de cdigo. El parmetro de configuracin hibernate.current_session_context_class define cul implementacin de org.hibernate.context.CurrentSessionContext debe ser usada. Note que, por compatibilidad con versiones anteriores, si este parmetro de configuracin no se asigna, sino que se configura org.hibernate.transaction.TransactionManagerLookup en su lugar, Hibernate usar el org.hibernate.context.JTASessionContext. Tpicamente, el valor de este parmetro simplemente nombra la clase de implementacin a usar; para las tres implementaciones de fbrica existen los respectivos nombres cortos: "jta", "thread" y "managed".
Captulo 3. Configuracin
Como Hibernate est diseado para operar en varios entornos diferentes, hay un gran nmero de parmetros de configuracin. Afortunadamente, la mayora tiene valores por defecto razonables, e Hibernate es distribuido con un archivo hibernate.properties de ejemplo en el directorio etc/, que muestra varias de las opciones. Simplemente coloque este ejemplo en su classpath y modifquelo a medida.
Una alternativa (a veces preferible), es especificar la clase mapeada, y dejar que Hibernate encuentre el documento de mapeo l solo:
Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class);
Entonces Hibernate buscar archivos de mapeo llamados /org/hibernate/auction/Item.hbm.xml y /org/hibernate enfoque elimina la necesidad de "hardcodear" los nombres de archivo.
org.hibernate.cfg.Configuration tambin permite
Configuration cfg = new Configuration() .addClass(org.hibernate.auction.Item.class) .addClass(org.hibernate.auction.Bid.class) .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect") .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test") .setProperty("hibernate.order_updates", "true");
sta no es la nica manera de pasarle propiedades de configuracin a Hibernate. Entre las muchas otras opciones, estn stas: Pasarle una instancia de java.util.Properties a Configuration.setProperties(). Colocar un archivo llamado hibernate.properties en un directorio raz del classpath. Configurar propiedades "de sistema" (System) usando java -Dproperty=value.
28 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La idea de org.hibernate.cfg.Configuration es ser un objeto que viva durante el "tiempo de arranque" (startup), para ser descartado luego, una vez que la SessionFactory haya sido creada.
Hibernate s le permite a la aplicacin instanciar ms de una org.hibernate.SessionFactory. Esto es til si se est usando ms de una base de datos.
No bien se haga algo que requiera acceso a la base de datos, una nueva conexin JDBC ser obtenida del "pool". Para que esto funcione, necesitamos pasarle algunas propiedades de conexin JDBC a Hibernate. Todos los nombres y semntica de las propiedades Hibernate estn definidos en la clase org.hibernate.cfg.Environment. Vamos a describir los valores ms importantes para configurar la conexin JDBC. Hibernate obtendr las conexiones (y las administrar en un "pool") usando un java.sql.DriverManager si usted configura las siguientes propiedades: Table 3.1. Hibernate JDBC Properties Nombre de la propiedad Propsito
hibernate.connection.driver_class clase del driver JDBC hibernate.connection.url hibernate.connection.username hibernate.connection.password hibernate.connection.pool_size URL de JDBC usuario de la base de datos clave del usuario de la base de datos nmero mximo de conexiones en el "pool"
El algoritmo que ya viene incluido en Hibernate para el "pooling" de conexiones es, sin embargo, bastante rudimentario. Fue concebido ms que nada para ayudarlo a dar los primeros pasos, no para ser usado en un sistema de produccin, ni siquiera para un test de performance. Ustde debera usar un "pool" de algn tercero para asegurar mayor redimiento y estabilidad. Simplemente reemplace la propiedad hibernate.connection.pool_size con propiedades especficas de la herramienta de "pooling" de su eleccin. Esto desactivar el "pooling" interno de Hibernate. Por ejemplo, usted podra elegir C3P0. C3P0 es una biblioteca para pool de conexiones distribuida junto con Hibernate en el direactorio lib. Hibernate usar su para efectuar el "pooling" de conexiones, si usted les asigna valores a las propiedades que empiezan con hibernate.c3p0.*. Si prefiere usar Proxool, refirase a las propiedades empaquetadas en hibernate.properties, o al sitio de web de Hibernate para mayor informacin. He aqu un ejemplo de un hibernate.properties para C3P0:
hibernate.connection.driver_class = org.postgresql.Driver
org.hibernate.connection.C3P0ConnectionProvider
29 de 198
http://www.hibernar.org/documentacion_es/castellano.html
hibernate.connection.url = jdbc:postgresql://localhost/mydatabase hibernate.connection.username = myuser hibernate.connection.password = secret hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statements=50 hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
Para usar "pooling" dentro de un servidor de aplicaciones, se debera configurar Hibernate de manera tal, que siempre obtenga las conexiones desde una fuente de datos javax.sql.Datasource registrada usando JNDI. Se necesitarn por lo menos las siguientes propiedades: Table 3.2. Propiedades para fuente de datos de Hibernate Nombre de la propiedad Propsito
hibernate.connection.datasource nombre JNDI de la fuente de datos hibernate.jndi.url hibernate.jndi.class hibernate.connection.username hibernate.connection.password URL del proveedor de JNDI (optativo) clase del InitialContextFactory de JNDI (optativo) usuario de la base de datos (optativo) clave del usuario de base de datos (optativo)
He aqu un ejemplo de un archivo hibernate.properties para configurar la fuente de datos JNDI en un servidor de aplicaciones:
hibernate.connection.datasource = java:/comp/env/jdbc/test hibernate.transaction.factory_class = org.hibernate.transaction.JTATransactionFactory hibernate.transaction.manager_lookup_class = org.hibernate.transaction.JBossTransactionManagerLooku hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
Las conexiones JDBC que se obtengan de una fuente de datos JNDI participarn automticamente de las transacciones "manejadas por el contenedor" del servidor de aplicaciones. Tambin se pueden agregar propiedades de conexin arbitrarias, afijando "hibernate.connection" al nombre de la propiedad de conexin. Por ejemplo, usted puede especificar una propiedad de conexin charSet (juego de caracteres), usando "hibernate.connection.charSet". Usted tambin puede definir su propia estrategia de plugin para obtener conexiones JDBC, implementando la interfaz org.hibernate.connection.ConnectionProvider, y especificando su propia implementacin a medida en la propiedad hibernate.connection.provider_class.
El nombre de clase de un org.hibernate.dialect.Dialect que le permita a Hibernate generar SQL optimizado para una BD relacional en particular. Tabla 3.4. Propiedades Hibernate de JDBC y Conexin hibernate.dialect Nombre de la propiedad hibernate.jdbc.fetch_size por ejemplo classname.completo.del.dialecto Propsito En laUn mayora de los casos, Hibernateel ser capaz de la implementacin valor distinto de 0 determina tamao de laelegir captura o "fetch" JDBC de (llama dialecto correcta, basndose en los metadatos devueltos por el driver JDBC. ). a Statement.setFetchSize()
30 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Nombre de la propiedad
Propsito Un valor distinto de 0 habilita el uso de actualizacionesn en lotes (batch updates) de JDBC2 por parte de Hibernate.
hibernate.jdbc.batch_size valores posibles se recomienda entre 5 y 30 Asgnele true a esta propiedad, si su driver JDBC devuelve conteos de fila al ejecutar executeBatch() (normalmente, es seguro hacerlo). Entonces, Hibernate usar "batched DML" (lenguaje de creacin de datos en lotes) para datos a los que se les haya asignado automtiacmente nmero de versin. El valor por defecto es false. valores posibles true | false Selecciona un org.hibernate.jdbc.Batcher hecho a medida. La mayora de las aplicaciones no necesita configurar esta propiedad. hibernate.jdbc.factory_class por ejemplo classname.of.BatcherFactory Habilita el uso de resultados JDBC2 navegables (scrollable resultsets) por parte de Hibernate. Esta propiedad slo es necesaria cuando se emplean conexiones JDBC provistas por el usuario. De otro modo, Hibernate usa los metadatos de la conexin. valores posibles true | false Indica que se usarn streams al leer o escribir cdigo binario, o tipos serializables desde o hacia JDBC. *propiedad a nivel de sistema* hibernate.jdbc.use_streams_for_binary valores posibles true | false Habilita el uso del PreparedStatement.getGeneratedKeys() de JDBC3 para capturar claves generadas en forma nativa luego de insertar. Requiere que a Requires JDBC3+ driver y a JRE1.4+, le sea asignado "false" si el driver que usted est usando tiene problemas con los generadores de identificadores. Por hibernate.jdbc.use_get_generated_keys defecto, intenta determinar las capacidades del driver usando los metadatos de conexin. valores posibles true|false El nombre de clase de un
org.hibernate.connection.ConnectionProvider
hibernate.jdbc.batch_versioned_data
hibernate.jdbc.use_scrollable_resultset
hibernate.connection.provider_class
provea conexiones JDBC a Hibernate. por ejemplo classname.of.ConnectionProvider Determina el nivel de aislamiento de las transacciones JDBC. Revise java.sql.Connection para averiguar qu valores tiene sentido asignar aqu, pero note que la mayora de las BD no soportan todos los niveles de aislamiento (isloation levels), y algunas definen niveles no estndar adicionales. por ejemplo 1, 2, 4, 8 Habilita la autocomisin (autocommit) para las conexiones JDBC en pool (no se recomienda).
hibernate.connection.isolation
31 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Nombre de la propiedad
Propsito Especifica cundo Hibernate debera liberar conexiones. Por defecto, una conexin JDBC es retenida hasta que la sesin es cerrada explcitamente o desconectada. Para fuentes de datos JTA en un servidor de aplicaciones, se debera usar after_statement para liberar conexiones agresivamente luego de cada llamado a JDBC. Para conexiones que no sean JTA, a menudo tiene sentido liberar las conexiones al final de cada transaccin, usando after_transaction. auto elegir after_statement para las estrategias JTA y CMT (manejadas por el contenedor), y after_transaction para las estrategias transaccionales JDBC.
hibernate.connection.release_mode
valores posibles auto (el valor por defecto) | on_close | after_transaction | after_statement Note que este valor slo afecta sesiones devueltas por SessionFactory.openSession. Para las que se hayan obtenido usando SessionFactory.getCurrentSession, lo que importa para determinar el modo de liberacin de conexiones, es la implementacin que se haya configurado de CurrentSessionContext. Vea la Seccin 2.5, Sesiones Contextuales
Le pasa el valor de propiedad JDBC <propertyName> a DriverManager.getConnection(). Le pasa la propiedad <nombreDeLaPropiedad> a la InitialContextFactory de JNDI.
Propsito El nombre de clase de un CacheProvider hecha a medida. por ejemplo classname.of.CacheProvider Optimiza el cach de 2do nivel para minimizar escrituras, al costo de realizar lecturas ms frecuentemente. Esta configuracin es ms til con cachs en "cluster", y en Hibernate3 se habilita por defecto con cachs en cluster. valores posibles true|false Habilita el cach de consultas. Cada consulta (query) debe ser individualmente configurada como "cacheable".
hibernate.cache.use_minimal_puts
hibernate.cache.use_query_cache
valores posibles true|false Puede ser usado para inhabilitar completamente el cach de 2do nivel, el cual est habilitado por defecto para toda clase que especifique un hibernate.cache.use_second_level_cache mapeo <cache> valores posibles true|false El nombre de una interfaz QueryCache personalizada, que por defecto es StandardQueryCache, la cual ya viene de fbrica.
hibernate.cache.query_cache_factory
32 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Nombre de la propiedad
hibernate.cache.region_prefix
Propsito Un prefijo a usar con los nombres de regin de cach de 2do nivel. por ejemplo prefix Fuerza a Hibernate a almacenar datos en el cach de 2do nivel en un formato ms legible.
hibernate.cache.use_structured_entries
Tabla 3.6. Propiedades para la configuracin de transaciones en Hibernate Nombre de la propiedad Propsito El nombre de clase de la TransactionFactory a usar con Hibernate (por defecto, JDBCTransactionFactory).
hibernate.transaction.factory_class
por ejemplo classname.of.TransactionFactory El nombre JNDI usado por JTATransactionFactory para obtener una UserTransaction JTA del servidor de aplicaciones. por ejemplo jndi/composite/name El nombre de clase de un TransactionManagerLookup. Obligatorio cuando se utiliza cach a nivel de la JVM, o se usa el generador hilo en un entorno JTA. por ejemplo classname.of.TransactionManagerLookup Si est habilitado, a la sesin le ser automtiamente hecho un "flush" antes de la fase de complecin de la transaccin. Es preferible usar el manejo de transacciones que ya viene hibernate.transaction.flush_before_completion incorporado, y el manejo automtico de contexto de sesiones; ver laSeccin 2.5, Sesiones contextuales. valores posibles true | false Si est habilitado, la sesin ser automticamente cerrada durante la fase de complecin de la transaccin. Es preferible usar el manejo de transacciones que ya viene incorporado, y el manejo automtico de contexto de sesiones; ver la Seccin 2.5, Sesiones contextuales. valores posibles true | false
jta.UserTransaction
hibernate.transaction.manager_lookup_class
hibernate.transaction.auto_close_session
Provee una estrategia personalizada para determinar el alcance (scope) respecto de cul es la sesin "actual". Vase la Seccin 2.5, Sesiones Contextuales para ms informacin sobre cules son las 3.4.1. Dialectos de SQL hibernate.current_session_context_class opciones que ya vienen incorporadas. Siempre se debera configurar la propiedad hibernate.dialect al valor correcto. de la subclase de jta | thread | managed | la clase valores posibles org.hibernate.dialect.Dialect que corresponda a su BD. Si se especifica un dialecto, Hibernate usar valores por personalizada defecto adecuados para otras de las propiedades listadas anteriormente, ahorrndole el esfuerzo de especificarlas
33 de 198
http://www.hibernar.org/documentacion_es/castellano.html
manualmente. Tabla 3.8. Dialectos SQL de Hibernate (hibernate.dialect) Base de datos relacional DB2 DB2 AS/400 DB2 OS390 PostgreSQL MySQL MySQL with InnoDB MySQL with MyISAM Oracle (any version) Oracle 9i/10g Sybase Sybase Anywhere Microsoft SQL Server SAP DB Informix HypersonicSQL Ingres Progress Mckoi SQL Interbase Pointbase FrontBase Firebird Dialecto
org.hibernate.dialect.DB2Dialect org.hibernate.dialect.DB2400Dialect org.hibernate.dialect.DB2390Dialect org.hibernate.dialect.PostgreSQLDialect org.hibernate.dialect.MySQLDialect org.hibernate.dialect.MySQLInnoDBDialect org.hibernate.dialect.MySQLMyISAMDialect org.hibernate.dialect.OracleDialect org.hibernate.dialect.Oracle9Dialect org.hibernate.dialect.SybaseDialect org.hibernate.dialect.SybaseAnywhereDialect org.hibernate.dialect.SQLServerDialect org.hibernate.dialect.SAPDBDialect org.hibernate.dialect.InformixDialect org.hibernate.dialect.HSQLDialect org.hibernate.dialect.IngresDialect org.hibernate.dialect.ProgressDialect org.hibernate.dialect.MckoiDialect org.hibernate.dialect.InterbaseDialect org.hibernate.dialect.PointbaseDialect org.hibernate.dialect.FrontbaseDialect org.hibernate.dialect.FirebirdDialect
34 de 198
http://www.hibernar.org/documentacion_es/castellano.html
causr que los smbolos true y false sean traducidos como valores enteros literates en el SQL que se genere.
hibernate.query.substitutions toLowercase=LOWER
Funcin Loguea todo el "lenguaje de modificacin de datos" (DML) de SQL a medida que ste es ejecutado. Loguea todos los parmetros JDBC Loguea todos los comandos SQL de definicin de datos (DDL) que hayan sido ejecutados Loguea el estado de todas las entidades (con un mximo de 20) asociadas con una sesin al momento de aplicarle "flush". Loguea toda la actividad del cach de 2do nivel Loguea toda actividad relacionada con transacciones Loguea toda adquisicin de recursos JDBC Loguea todo AST de HQL y SQL durante la revisin (parsing) de las consultas
35 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Category (categora)
org.hibernate.secure org.hibernate
Funcin Loguea todas las autorizaciones para requests JAAS Loguea todo (un montn de informacin, pero muy til a para detectar y solucionar problemas).
org.hibernate.SQL,
Al desarrollar aplicaciones con Hibernate, se debera trabajar casi siempre con debug habilitado para o, alternativamente, habilitar la propiedad hibernate.show_sql enabled.
36 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<class-cache class="org.hibernate.auction.Item" usage="read-write"/> <class-cache class="org.hibernate.auction.Bid" usage="read-only"/> <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/> </session-factory> </hibernate-configuration>
Como se puede ver, la ventaja de este enfoque es externalizar la configuracin de los nombres de los acrhivos de mapeo. hibernate.cfg.xml tambin es ms conveniente para ajustar los valores del cach. Note que usar hibernate.cfg.xml o hibernate.properties es indistinto, excepto por los beneficios mencionados de usar una sintaxis XML. Con la configuracin XML, hacer arrancar a Hibernate es tan simple como:
SessionFactory sf = new Configuration().configure().buildSessionFactory();
37 de 198
http://www.hibernar.org/documentacion_es/castellano.html
stas son las tres opciones estndar, que ya vienen incluidas "de fbrica":
org.hibernate.transaction.JDBCTransactionFactory
delega a las transacciones JDBC de la base de datos (es el valor por defecto)
org.hibernate.transaction.JTATransactionFactory
delega a la transaccin manejada por el contenedor (container-managed) si hay una tranasccin en proceso en este contexto (por ejemplo, un mtodo de un Session EJB), en caso contrario, crea una nueva transcin, y se utilizan transacciones manejadas por bean.
org.hibernate.transaction.CMTTransactionFactory
delega a transacciones JTA manejadas por el contenedor Usted tambin puede definir sus propias estrategias transaccionales (por un servicio de transacciones CORBA, por ejemplo). Algunas caractersticas de Hibernate (por ejemplo, cach de 2do nivel, sesiones contextuales con JTA, etc) requieren aceso al TransactionManager de JTA en un entorno administrado. En un servidor de aplicaciones, usted tiene que especificar cmo Hibernate obtendr una referencia al TransactionManager, dado que en J2EE no hay un mecanismo estndar y nico. Tabla 3.10. TransactionManagers de JTA Fbrica de transacciones
org.hibernate.transaction.JBossTransactionManagerLookup org.hibernate.transaction.WeblogicTransactionManagerLookup org.hibernate.transaction.WebSphereTransactionManagerLookup org.hibernate.transaction.WebSphereExtendedJTATransactionLookup org.hibernate.transaction.OrionTransactionManagerLookup org.hibernate.transaction.ResinTransactionManagerLookup org.hibernate.transaction.JOTMTransactionManagerLookup org.hibernate.transaction.JOnASTransactionManagerLookup org.hibernate.transaction.JRun4TransactionManagerLookup org.hibernate.transaction.BESTransactionManagerLookup
Servidor de aplicaciones JBoss Weblogic WebSphere WebSphere 6 Orion Resin JOTM JOnAS JRun4 Borland ES
38 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Si usted usa una fbrica de sesiones JNDI, un EJB o cualquier otra clase puede obtenerla usando el JNDI lookup. Le recomendamos que, en un entorno administrado, use JNDI para obtener sus fbricas de sesiones, y en cualquier otro entorno, un singleton esttico. Para escudar su aplicacin de estos detalles, tambin le recomendamos que oculte el cdigo que efectivamente realiza el "lookup" dentro de una clase utilitaria, (como por ejemplo, HibernateUtil.getSessionFactory()). Note que dicha clase es tambin una buena manera de hacer arrancar Hibernate (captulo 1).
39 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Este archivo es desplegado en un directorio llamado META-INF, y empaquetado en un archivo JAR con la extensin .sar (service archive o "archivo de servicio). Sus EJB (usualmente session beans) pueden ser conservados en su propio archivo JAR, pero este archivo JAR de EJB se puede incluir en el archivo principal de servicio para obtener una nica desplegable "en caliente" (hot deployment). Consulte la documentacin del servidor de aplicaciones JBoss para ms informacin acerca del servicio JMX y el despliegue de EJB.
private Cat mother; private Set kittens = new HashSet(); private void setId(Long id) { this.id=id; } public Long getId() { return id; } void setBirthdate(Date date) { birthdate = date; } public Date getBirthdate() { return birthdate; } void setWeight(float weight) { this.weight = weight; } public float getWeight() { return weight; } public Color getColor() { return color; } void setColor(Color color) {
40 de 198
http://www.hibernar.org/documentacion_es/castellano.html
this.color = color; } void setSex(char sex) { this.sex=sex; } public char getSex() { return sex; } //litter=en ingls, camada void setLitterId(int id) { this.litterId = id; } public int getLitterId() { return litterId; } void setMother(Cat mother) { this.mother = mother; } public Cat getMother() { return mother; } //kittens=gatitos void setKittens(Set kittens) { this.kittens = kittens; } public Set getKittens() { return kittens; } // addKitten no es requerido por Hibernate public void addKitten(Cat kitten) { kitten.setMother(this); kitten.setLitterId( kittens.size() ); kittens.add(kitten); } }
Le recomendamos que declare propiedades indentificador nombradas de una manera consistente, en sus clases
41 de 198
http://www.hibernar.org/documentacion_es/castellano.html
persistentes. Mas an, le recomendamos que use un tipo anulable (es decir, no primitivo).
4.1.4. Declare mtodos de acceso y "mutadores" (accessors, mutators) para los campos persistentes.
Cat declara mtodos de acceso para todos sus campos persistentes. Muchos otras herramientas de ORM persisten directamente sus variables de instancia. Nosotros creemos que es mejor proveer un nivel de aislamiento entre el esquema relacional y la estructura interna de datos de la clase. Por defecto, Hibernate persiste propiedades del tipo JavaBean, y reconoce nombres de mtodo de la forma getAlgo, isAlgo y setAlgo. Si es necesario, usted puede revertir esto, y permitir el acceso directo, para propiedades especficas,
No se necesita que las propiedaes sean declaradas como pblicas. Hibernate puede persistir una propiedad con un par get/set que tenga acceso por defecto (package) o privado.
42 de 198
http://www.hibernar.org/documentacion_es/castellano.html
basados en el identificador) cambiaran, rompiendo el contrato del Set. Vea el sitio de web de Hibernate para una discusin completa de este problema. Note que esto no es un problema de Hibernate, sino parte de la semntica normal de Java en lo que respecta a la igualdad e identidad de los objetos. Nosotro recomendamos implementar equals() y hashCode() utilizando una igualdad "por clave de negocio". Esto quiere decir, que estos mtodos comparen solamente propiedades que formen la clave de negocio, una clave que identificara a nuestro objeto en el mundo real (una clave candidata natural).
public class Cat { ... public boolean equals(Object other) { if (this == other) return true; if ( !(other instanceof Cat) ) return false; final Cat cat = (Cat) other; if ( !cat.getLitterId().equals( getLitterId() ) ) return false; if ( !cat.getMother().equals( getMother() ) ) return false; return true; } public int hashCode() { int result; result = getMother().hashCode(); result = 29 * result + getLitterId(); return result; } }
Note que la clave de negocio no necesita ser tan slida como una clave primaria de la base de datos. (vea Seccin 11.1.3, Considerar la identidad de un objeto). Propiedades que sean nicas o inmutables, usualmente son buenas candidatas a "clave de negocio".
43 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</class>
</hibernate-mapping>
Note que, incluso si las asociaciones son declaradas usando nombres de clases de destino, el tipo de destino de una asociacin tambin puede ser una entidad dinmica en lugar de un POJO. Despus de configurar el modo de entidad por defecto a dynamic-map para esta SessionFactory, podemos trabajar con "mapas de mapas" en tiempo de ejecucin.
Session s = openSession(); Transaction tx = s.beginTransaction(); Session s = openSession(); // Crear un cliente Map david = new HashMap(); david.put("name", "David"); // Crear una organizacin Map foobar = new HashMap(); foobar.put("name", "Foobar Inc."); // Vincular a ambos david.put("organization", foobar); // Grabar a ambos s.save("Customer", david); s.save("Organization", foobar); tx.commit(); s.close();
La ventaja de un mapeo dinmico es acelerar el tiempo de entrega, para prototipos, sin la necesidad de implementar clases de entidades. De todos modos, se pierde el chequeo de tipos en tiempo de compilacin, y esto muy probablemente causar varias excepciones en tiempo de ejecucin. Gracias al mapeo de Hibernate, el esquema de base de datos puede ser fcilmente normalizado y saneado, permitiendo agregar una implementacin apropiada de modelo de dominio encima, ms adelante. Los modos de representacin de entidad tambin pueden ser configurados por sesin:
Session dynamicSession = pojoSession.getSession(EntityMode.MAP); // Crea un cliente Map david = new HashMap(); david.put("name", "David"); dynamicSession.save("Customer", david); ... dynamicSession.flush(); dynamicSession.close() ... // Contina con la sesin "POJO"
Dese cuenta por favor de que el llamado a getSession() usando un EntityMode determinado, es cosa de la API de Session API, no de SessionFactory. De esta manera, la nueva sesin comparte la misma conexin JDBC subyacente, y otra informacin de contexto. Esto vuelve innecesario invocar flush() y close() en la sesin secundaria, y le deja el manejo de transacciones y conexiones a unidad de trabajo primaria. Se puede encontrar ms informacin sobre las capacidades de representacin con XML en Captulo 18, Mapeo XML.
4.5. T-uplizadores
org.hibernate.tuple.Tuplizer, y sus sub-interfaces, son los encargados de manejar una representacin en particular de un fragmento de datos, dado el org.hibernate.EntityMode de dicha representacin. Si un determinado fragmento de
datos se concibe como una estructura de datos, entonces un t-uplizador es lo que sabe cmo crear dicha estructura. Por ejemplo, para el modo de entidad "POJO", el t-uplizador correspondiente sabe cmo crear el POJO mediante su constructor, y cmo acceder a las propiedades de POJO usando los mtodos de acceso. Hay dos t-uplizadores de alto nivel, representados por las interfaces org.hibernate.tuple.entity.EntityTuplizer y
44 de 198
http://www.hibernar.org/documentacion_es/castellano.html
org.hibernate.tuple.component.ComponentTuplizer.
Los EntityTuplizers see encargan de manejar los "contratos" recin descritos para las entidades, mientras que los ComponentTuplizers hacen lo propio para los componentes.
El usuario puede, asimismo, insertar sus propios t-uplizadores. Tal vez usted desee que par el modo de entidad "dynamic-map" se utilice una implementacin de java.util.Map que no sea java.util.HashMap, o tal vez usted necesita que se utilice una estrategia de generacin de proxies distinta de la que viene de fbrica. Las definiciones de t-uplizer van adjuntas a la entidad o mapeo de componentes que estn destinadas a manejar. Volviendo al ejemplo de la entidad "cliente" (customer).
<hibernate-mapping> <class entity-name="Customer"> <!-Reemplaza el t-uplizer de modo de entidad dynamic-map por el de entity-mode --> <tuplizer entity-mode="dynamic-map" class="CustomMapTuplizerImpl"/> <id name="id" type="long" column="ID"> <generator class="sequence"/> </id> <!-- otras propiedades--> ... </class> </hibernate-mapping>
public class CustomMapTuplizerImpl extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer { // suplanta al mtodo buildInstantiator() para "enchufar" nuestro mapeo hecho a medida ... protected final Instantiator buildInstantiator(org.hibernate.mapping.PersistentClass mappingInf return new CustomMapInstantiator( mappingInfo ); } //suplanta a generateMap() para devolver nuestro map hecho a medida private static final class CustomMapInstantiator extends org.hibernate.tuple.DynamicMapInstanti protected final Map generateMap() { return new CustomMap(); } } }
4.6. Extensiones
A HACER: documentar el framework de extensiones de usuario y paquetes proxy.
45 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<hibernate-mapping package="eg"> <class name="Cat" table="cats" discriminator-value="C"> <id name="id"> <generator class="native"/> </id> <discriminator column="subclass" type="character"/> <property name="weight"/> <property name="birthdate" type="date" not-null="true" update="false"/> <property name="color" type="eg.types.ColorUserType" not-null="true" update="false" <property name="sex" not-null="true" update="false"/> <property name="litterId" column="litterId" update="false"/> <many-to-one name="mother" column="mother_id" update="false"/> <set name="kittens" inverse="true" order-by="litter_id"> <key column="mother_id"/> <one-to-many class="Cat"/> </set> <subclass name="DomesticCat" discriminator-value="D"> <property name="name" type="string"/> </subclass> </class> <class name="Dog"> <!-- ac podra ir un mapeo para Perro --> </class> </hibernate-mapping>
(N.del T): "eg" son las siglas de "exempli gratia", una locucin latina que en ingls hace las veces de "por ejemplo". A lo largo de esta documentacin, "eg" se usa como el paquete por defecto para los ejemplos de cdigo. Ahora discutiremos el contenido del documente de mapeo. Slo describiremos los elementos y atributos del documento que son usados por Hibernate en tiempo de ejecucin. El documento de mapeo tambin contiene algunos atributos optativos adicionales, y elementos que afectan los esquemas de BD exportados por herramientas de exportacin de esquemas (por ejemplo, el atributo not-null).
5.1.1.1. EntityResolver
Como se mencion anteriormente, Hibernate primero intentar resolver la DTD en su classpath. La manera en que lo hace, es registrando una implementacin personalizada de org.xml.sax.EntityResolver con el SAXReader que usa para leer los archivos xml. Este EntityResolver perosnalizado reconoce dos espacios de nombre de systemId diferentes. un "espacio de nombre" (namespace) de Hibernate es reconocido siempre que el resolver encuentra un systemId qie comience con http://hibernate.sourceforge.net/; el resolver intenta resolver estas entidades mediante el classloader que haya cargado las clases de Hibernate. un espacia de nombre de usuario se reconoce siempre que el resolver encuentra un systemId que use un protocolo de URL classpath://; el resolver intentar resolver esas entidades a travs de (1) el classloader del contexto de thread actual, y (2), el classloader que haya cargado las clases de Hibernate. Un ejemplo de utilizacin de un espacio de nombre (namespace) hecho a medida:
46 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [ <!ENTITY types SYSTEM "classpath://your/domain/types.xml"> ]> <hibernate-mapping package="your.domain"> <class name="MyEntity"> <id name="id" type="my-custom-id-type"> ... </id> <class> &types; </hibernate-mapping>
En donde types.xml es un recurso en el paquete your.domain y contiene una definicin de tipo a medida typedef.
5.1.2. hibernate-mapping
Este elemento tiene varios atributos optativos. Los atributos schema y catalog especifican que las tablas a las que este mapeo se refiere pertenecen al esquema o catlogo indicado. Si se especifican, los nombres de tabla sern calificados con el esquema y/o catlogo dados. Si no estn presentes, los nombres de tabla no sern calificados. El atributo defaultcascade especifica qu estilo de propagacin en cascada debera asumirse para las propiedades y colecciones que no especifiquen un atributo cascade. El atributo auto-import nos permite, por defecto, usar un nombre de clase no calificado en el lenguaje de consultas.
<hibernate-mapping schema="schemaName" catalog="catalogName" default-cascade="cascade_style" default-access="field|property|ClassName" default-lazy="true|false" auto-import="true|false" package="package.name" />
(1)
schema (optativo): El nombre
de un esquema de BD
(2)
catalog
(3)
default-cascade (optativo - por
(4) defecto, property): La estrategia que Hibernate debera usar para acceder a todas las propiedades. Puede ser una implementacn personalizada de PropertyAccessor. (5)
default-lazy (optativo - por defecto, true): El valor por defecto para los atributos lazy no especificados de clases y colecciones. default-access (optativo - por
(6)
auto-import
(optativo - por defecto true): Especifica si se puede usar nombres de clase no calificados (de clases en el mapeo) el en lenguaje de consultas.
(7)
package (optativo): Especifica un prefijo de paquete a ser asumido, para las clases no calificadas en el documento de mapeo.
47 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Si usted tiene dos clases persistentes cuyo nombre (no calificado) es el mismo, debera configurar auto-import="false". Hibernate lanzar una excepcin si usted le intenta asignar dos clases al mismo nombre "imported". Note que el elemento hibernate-mapping le permite anidar los mapeos de varias clases persistentes, como se muestra anteriormente. Sin embargo, mapear slo una clase persistente (o jerarqua de clases) por archivo de mapeo es una costumbre ms establecida (y lo que algunas herramientas esperan). Por ejemplo, Cat.hbm.xml, Dog.hbm.xml, o, si se usa herencia, Animal.hbm.xml.
5.1.3. class
Se puede declarar una clase persistente usando el elemento class.
<class name="ClassName" table="tableName" discriminator-value="discriminator_value" mutable="true|false" schema="owner" catalog="catalog" proxy="ProxyInterface" dynamic-update="true|false" dynamic-insert="true|false" select-before-update="true|false" polymorphism="implicit|explicit" where="arbitrary sql where condition" persister="PersisterClass" batch-size="N" optimistic-lock="none|version|dirty|all" lazy="true|false" entity-name="EntityName" check="arbitrary sql check condition" rowid="rowid" subselect="SQL expression" abstract="true|false" node="element-name" /> (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21)
(1)
name (optativo): El nombre
(totalmente calificado) de la clase (o interfaz) persistente de Java. Si este atributo est ausente, se asume que no se trata de una entidad POJO.
(2)
table (optativo
(3)
discriminator-value (optativo
- por defecto, el nombre de la clase): Un valor que distingue entre subclases individuales, usado para comportamiento polimrfico. null y not null tambin son valores aceptables.
(4)
mutable
(optativo, por defecto, true): Especifica si las instancias de esta clase son o no mutables.
(5)
schema (optativo): Suplanta
(6)
catalog
(7)
proxy (optativo): Especifica
una interfaz a utilizar para los proxies de inicializacin perezosa. Se puede especificar
48 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(8)
dynamic-update (optativo, por defecto false): Especifica que los comandos SQL UPDATE deberan ser generados en tiempo de ejecucin, y contener slo aqullas comlumnas cuyos valores hayan cambiado.
(9)
dynamic-insert (optativo, por defecto, false): Especifica que se deberan generar comandos SQL INSERT en tiempo de ejecucin, conteniendo slo las columnas cuyos valores son no nulos.
(10)
select-before-update (optativo, por defecto false): Especifica que Hibernate nunca debera ejecutar un UPDATE SQL a menos que est seguro de que en realidad se est modificando algn objeto. En algunos casos (en realidad, slo cuando un objeto transitorio ha sido asociado con una nueva sesin usando update()), esto significa que Hibernate ejecutar un comando SQLSELECT adicional, para determinar si un en realidad un UPDATE es
necesario. (11)
polymorphism (optativo,
(12)
where (optativo)
especifica una condicin SQL WHERE arbitraria a ser usada cuando se seleccionen objetos de esta
clase. (13)
persister
(14)
batch-size
(optativo, por defecto, 1) especifica un "tamao de lote" para capturar instancias de esta clase por identificador.
(15)
optimistic-lock (optativo, por
(16)
lazy (optativo): La
(17)
entity-name (optativo, por defecto, el nombre de la clase): Hibernate3 permite que una clase sea mapeada muchas veces (potencialmente, a distintas tablas), y permite mapeos de entidades qie estn representados por Maps a nivel de Java, En estos casos, se debera proveer un nombre arbitrario explcito para la entidad. Vea Seccin 4.4, Modelos dinmicos y Captulo 18, Mapeo XML for more information.
(18)
check (optativo): Una
expresin SQL arbitraria usada para generar una constraint tipo check multifila a usar durante la generacin automtica del esquema.
(19)
rowid (optativo): Hibernate
puede usar los vulgarmente llamados ROWIDs, en aquellas DB que lo soporten. Por ejemplo, en Oracle, Hibernate puede usar la columna adicional rowid para actualizar ms rapidamente, si usted configura esta opcin a rowid. Un ROWID es un detalle de implementacin, y representa la ubicacin fsica de una t-upla alamacenada.
(20)
subselect (optativo): Mapea una entidad inmutable y de slo-lectura a un subselect de la base de datos. Es til si se quiere tener una vista en lugar de una tabla, pero no se cuenta con una vista en la base de datos. Vea ms adelante para ms informacin.
(21)
abstract
49 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Es perfectamente aceptable que la clase persistente nombrada sea une interfaz. En ese caso, se debe declarar la clase implementadora usando el elemento <subclass>. Se puede persistir cualquier clase anidada (inner class) esttica. Se debera especificar el nombre de este tipo de clases usando la nomenclatura estndar, es decir com.Mi$ClaseInterna. Las clases inmutables (mutable="false") no pueden ser actualizadas o borradas por la aplicacin. Esto le permite a Hibernate realizar algunas optimizaciones menores. El atributo optativo proxy permite la inicializacin perezosa de instancias persistentes de la clase. Inicialmente, Hibernate devolver proxies CGLIB, que implementan la interfaz mencionada. El objeto persistente propiamente dicjo ser cargado cuando se invoque un mtodo del proxy. Vea "Inicializar colecciones y proxies" ms adelante. Polimorfismo implcito significa que cualquier consulta que nombre a una superclas o interfaz devolver instancias de la clase misma. Polimorfismo explcito significa que instancias de esta clase slo sern devueltas por consulta que nombren a esta clase, y que las consultas que nombren a esta clase devolvern slo instancias de subclases que hayan sido mapeados como <subclass> o <joined-subclass>. Para la mayora de los casos, el valor por defecto polymorphism="implicit", es lo ms apropiado. El polimorfismo explcito es til cuando hay dos clases diferentes mapeadas a la misma tabla (lo cual permite tener una clase "de peso ligero" que contenga un subconjunto de las columnas de la tabla). El atributo persister le permite personalizar la estrategia de persistencia usada para la clase. Por ejemplo, usted puede especificar su propia subclase de org.hibernate.persister.EntityPersister, o hasta puede proveer una implementacin completamente nueva de la interfaz org.hibernate.persister.ClassPersister que persista, por ejemplo, valindose de una llamada a un procedimiento almacenado de base de datos, o usando serializacin a archivos planos, o LDAP. Vea org.hibernate.test.CustomPersister para un ejemplo simple (de "persistencia" a una Hashtable). Note que las asignaciones de valores dynamic-update y dynamic-insert no son heredadas por la subclases, as que pueden ser especificadas en los elementos <subclass> o <joined-subclass>. Estos valores pueden incrementar la performance en algunos casos, pero reducirla en otros. selos con cuidado. El uso de select-before-update normalmente bajar la performance. Es muy til para evitar que un "update trigger" de la base de datos sea invocado innecesariamente si usted est revinculando todo un rbol de instancias desprendidas a una sesin. Si usted habilita dynamic-update, tendr las siguientes opciones de "locking" optimista
version verifica all
verifica todas las columnas verifica las columnas que hayan cambiado, permitiendo algunas modificaciones concurrentes
dirty none
Recomendamos muy fuertemente que se utilicen las columnas de versin/timestamp para locking optimista con Hibernate. sta es la estrategia ptima en lo que a performace se refiere, y es la nica estrategia que maneja correctamente las modificaciones que se hagan a instancias desprendidas (es decir, cuando se use Session.merge() ). Para un mapeo Hibernate, no hay diferencia entre una vista y una tabla de la base de datos. Como es de esperarse, esto es transparente a nivel de base de datos (note que algunas base de datos no soportan vistas correctamente, especialmente actualizaciones de stas). A veces usted quiere usar una vista, pero no puede crear una en la base de datos (por ejemplo, con un esquema heredado anticuado). En este caso, usted puede mapear una entidad inmutable y de slo-lectura a una expresin "subselect" de SQL.
<class name="Summary"> <subselect> select item.name, max(bid.amount), count(*) from item join bid on bid.item_id = item.id group by item.name </subselect> <synchronize table="item"/> <synchronize table="bid"/> <id name="name"/> ... </class>
50 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Declara las tablas contra las cuales se ha de sincronizar esta entidad, asegurndose de que el auto-flush ocurra correctamente. y que las consultas efectuadas contra la entidad derivada no devuelvan datos vencidos. En el mapeo, <subselect> est disponible como atributo y como elemento anidado.
5.1.4. id
Las clases mapeadas deben declarar la columna de clave primaria de la tabla en la base de datos. La mayora de las clases tambin tendrn una propiedad del estilo JabaBeans conteniendo el didentificador nico de una instancia determinada. El elemento <id> define el mapeo de esa propiedad a la columna de clave primaria.
<id name="propertyName" type="typename" column="column_name" unsaved-value="null|any|none|undefined|id_value" access="field|property|ClassName"> node="element-name|@attribute-name|element/@attribute|." <generator class="generatorClass"/> </id> (1) (2) (3) (4) (5)
(1)
name (optativo): El nombre
de la propiedad indentificadora.
(2)
type (optativo): Un nombre
(3)
column (optativo, por defecto,
(4)
unsaved-value (optativo, por defecto, un valor "adecuado"): Una propiedad identficadora que indica que una instancia ha sido recientemente instanciada (no grabada), distinguindola de las instancias desprendidas que fueron grabadas o cargadas en una sesin previa.
(5)
access (optativo, por defecto, property): La
propiedad.
Si falta el atributo name , se asume que la clase no posee propiedad indentificadora. El atributo unsaved-value casi nunca se usa en Hibernate3. Hay una declaracin alternativa de <composite-id>, para permitur el acceso a datos de formato anticuado, con clave compuesta. Desaconsejamos su uso en cualquier otro caso.
5.1.4.1. Generator
El elemento opcional hijo <generator> nombra a la clase de Java que se usa para generar los identificadores nicos de la clase persistente. Si hace falta algn parmetro para inicializar la instancia del generador, se pasa usando el elemento <param>.
<id name="id" type="long" column="cat_id"> <generator class="org.hibernate.id.TableHiLoGenerator"> <param name="table">uid_table</param> <param name="column">next_hi_value_column</param> </generator> </id>
Todos los generadores implementan la interfaz org.hibernate.id.IdentifierGenerator. Es una interfaz muy simple;
51 de 198
http://www.hibernar.org/documentacion_es/castellano.html
algunas aplicaciones podran elegir proveer su propia implementacin a medida. De todos modos, Hibernate provee una variedad de implementaciones que ya vienen incluidas. Los siguientes son los apodos mnemnicos para estos generadores ya incluidos:
increment
genera identificadores de tipo long, short o int que son nicos solamente cuando ningn otro proceso est insertando datos en la misma tabla. No usar en un cluster.
identity
soporta "columnas de identidad" (identity) en DB2, MySQL, MS SQL Server, Sybase e HypersonicSQL. El identificador que se devuelve es de tipo long, short o int.
sequence
usa una secuencia (sequence) en DB2, PostgreSQL, Oracle, SAP DB, McKoi o un generador en Interbase. El identificador que se devuelve es de tipo long, short o int
hilo
usa un algoritmo hi/lo para generar identificadores de los tipos long, short o int eficientemente, dada una tabla y una columna (por defecto, hibernate_unique_key y next_hi respectivamente) como fuente de los valores "hi". El algorithmo hi/lo genera identificadores que son nicos slo para una base de datos en particular.
seqhilo
usa un algoritmo hi/lo para generar identificadores de los tipos long, short o int eficientemente, dada una secuencia (sequence) nombrada de base de datos.
uuid
usa un algoritmo UUID de 128 bits para generar identificadores de tipo string, nicos para toda una red (se usa la direccin de IP). El UUID es codificado como una cadena de 32 dgitos hexadecimales.
guid
usa una cadena GUID generada por la base de datos, en MS SQL Server y MySQL.
native
elije identity, sequence o hilo, dependiendo de las capacidades de la base de datos subyancente.
assigned
deja que sea la aplicacin la que asigne el identificador al objeto antes de que save() sea llamado. sta es la estrategia por defecto si no se especifica un elemento <generator>.
select
obtiene una clave primaria asignada por un trigger de la base de datos, seleccionando la fila por alguna clave nica y obteniendo el valor de clave primaria.
foreign
usa el identificador de algn otro objeto asociado. Normalmente se usa en conjuncin con una asocoacin <one-to-one> por clave primaria.
sequence-identity
una generacin de secuencias especializada que utiliza una sequencia de base de datos para la generacin del valor en s, pero lo combina con el mtodo de JDBC3 getGeneratedKeys para devolver el valor final, como parte del comando INSERT. Esta estrategia slo es soportada, que sepamos, por los drivers de Oracle 10g diseados para JDK1.4. Note que los comentarios en estos comandos estn inhabilitados, debido a un error en los drivers de Oracle.
52 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Desafortunadamente, usted no puede usar hilo cuando le provee su propia conexin (Connection) a Hibernate. Cuando Hibernate use una fuente de datos de un servidor de aplicaciones para obtener conexiones inscriptas en JTA, usted deber configurar adecuadamente la hibernate.transaction.manager_lookup_class.
La estrategia native produce un desarrollo ms porttil entre distintas plataformas (cross-platform), ya que elige entre identity, sequence e hilo, dependiendo de las capadidades de la DB subyacente.
53 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Elegir el generador assigned, hace que Hibernate use unsaved-value="undefined", forzndolo a determinar si una instancia es transitoria o desprendida, a menos que haya una propiedad "version" o "timestamp", o usted defina Interceptor.isUnsaved().
(N del T): el nmero de seguridad social o SSN es un nmero nico asignado por el Estado a cada persona en EE.UU. En el ejemplo precedente, hay un valor de propiedad nico llamado socialSecurityNumber, definido por la clase, como clave natural, y una clave primaria en la tabla llamada person_id, cuyo valor es generado por un trigger que seleciona dicho nmero.
inicial a ser devuelto por la tabla/secuencia. En trminos de la creacin de la secuencia, esto es anlogo a la clusula tpica "STARTS WITH".
llamadas ulteriores a la tabla/secuencia. En trminos de la creacin de la secuencia, esto es anlogo a la clusula tpica "INCREMENT BY".
force_table_use (optativo, por defecto, false): Deberamos forzar el uso de una tabla como estructura de respaldo, incluso si el dialecto soporta secuencias? value_column (optativo, por defecto, next_val): Slo relevante para estructuras de tabla! El nombre de la columna en la tabla usada para almacenar el valor. optimizer (optativo, por
El segundo de estos nuevos generadores es org.hibernate.id.enhanced.TableGenerator, el cual ha sido primariamente concebido en reemplazo del generador table, (aunque, en realidad, funciona mucho ms como un org.hibernate.id.MultipleHiLoPerTableGenerator). Secundariamente, ha sido concebido como una reimplementacin de org.hibernate.id.MultipleHiLoPerTableGenerator que utiliza la nocin de optimizadores "enchufables" (pluggable). En esencia, este generador define una tabla que es capaz de contener mltiples valores de
54 de 198
http://www.hibernar.org/documentacion_es/castellano.html
incremento distintos, usando mltiples registros con claves diferentes. Este generador tiene varios parmetros de configuracin:
table_name (optativo, por value_column_name
(optativo, por defecto, next_val): El nombre de la columna en la tabla, que ser usada para
contener el valor.
segment_column_name (optativo, por defecto, sequence_name): El nombre de la columna en la tabla que set usada para contener la "clave de segmento". Este es el valor que identifica de manera distintiva qu valor de incremento usar. segment_value (optativo, por defecto, default): El valor de "clave de segmento" del cual queremos extraer valores de incremento para este generador. segment_value_length (optativo, por defecto, 255): Usado para la generacinde esquemas; el tamao del campo de esta "clave de segmento". initial_value (optativo, por defecto, 1): El valor
inicial a ser devuelto por la tabla. por el cual las llamdas subsiguientes a la tabla deberan diferir.
5.1.7. composite-id
<composite-id name="propertyName" class="ClassName" mapped="true|false" access="field|property|ClassName"> node="element-name|." <key-property name="propertyName" type="typename" column="column_name"/> <key-many-to-one name="propertyName class="ClassName" column="column_name"/> ...... </composite-id>
En una tabla con clave compuesta, se puede mapear varias de sus propiedades como identificadores. El elemento <composite-id> acepta mapeos de propiedades <key-property> y mapeos como elementos hijos.
<composite-id> <key-property name="medicareNumber"/>
55 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Su clase persistente debe reemplazar equals() y hashCode() para implementar igualdad entre identificadores compuestos. Tambin debe implementar Serializable. Desafortunadamente, este abordaje de los identificadores significa que el objeto persistente es su propio identificador. No hay otro "puntero" al objeto que no sea el objeto mismo. Se debe instanciar el objeto persistente mismo, y poblarle sus propiedades identificadores antes de poder cargarlo en estado persistente asociado con una clave primaria. A este enfoque lo llamamos "identificador compuesto incrustado (embedded)", y lo desaconsejamos para cualquier aplicacin seria. El segundo abordaje es lo que llamamos "identificador compuesto mapeado", en donde las propiedades identificador usadas dentro del <composite-id> son duplicadas tanto en la clase persistente como en una clase identificadora separada.
<composite-id class="MedicareId" mapped="true"> <key-property name="medicareNumber"/> <key-property name="dependent"/> </composite-id>
(N.del.T): "Medicare" es el nombre en ingls del seguro estatal de salud en EE.UU. y otros pases. En este ejemplo, tanto la clase del identificador compuesto, MedicareId, como la clase de la entidad misma tienen propiedades llamadas medicareNumber y dependent. La clase identificadora debe reemplazar equals() y hashCode() e implementar Serializable. La desventaja de este abordaje es obvia: duplicacin de cdigo. Los siguientes atributos son usados para especificar un identificador compuesto mapeado:
mapped
(optativo, por defecto, false): indica que se usa un identificador mapeado compuesto, y que los mapeos contenidos de propiedades se refieren tanto a la clase entidad como a la clase del identificador compuesto.
class (optativo, pero requerido para un identificador compuesto mapeado): La clase usada como identificador compuesto.
Vamos a describir una tercera manera, an ms conveniente de enfocar el tema de los modificadores compuestos, en donde el identificador compuesto es implementado como una clase componente en Seccin 8.4, Componentes como identificadores compuestos. Los atributos descritos a continuacin se aplican slo a este enfoque alternativo:
name (optativo, pero obligatorio para este abordaje): Una propiedad de tipo "componente" que contiene el identificador compuesto (ver captulo 9). access
(optativo, por defecto, property): La estrategia que Hibernate debera usar para acceder al valor de las propiedades.
class (optativo, por defecto, el tipo de propiedad determinado por reflexin): La clase componente usada como identificador compuesto (ver la seccin siguiente).
Este tercer abordaje, un compoennte identificador, es el que recomendamos para casi todas las aplicaciones.
56 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(1)
column (optativo, por defecto, class)
(2)
type (optativo, por defecto, string) un nombre
(3)
force (optativo, por defecto, false) "fuerza" a Hibernate a especificar valores permitidos de discriminador cuando captura todas las instancias de la clase raz.
(4)
insert (optativo, por defecto, true) asgnele false si su columna de discriminador tambn forma parte de un identificador compuesto mapeado (le dice a Hibernate que no incluya a esta columna en el INSERT).
(5)
formula (optativo) una expresin SQL arbitraria que se ejecuta cuando un tipo tiene que ser evaluado. Permite discriminacin basada en contenido.
Los verdaderos valores de la columna del discriminador son especificados por el atributo discriminator-value de los elementos <class> y <subclass>. El atributo force solamente es til si la tabla contiene valores de discriminador "adicionales" que no estn mapeados a una clase persistente. ste no es casi nunca el caso. Usando el atributo formula, se puede declarar una expresin SQL arbitraria que puede ser usada para evaluar el tipo de una fila.
<discriminator formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end" type="integer"/>
(1)
column (optativo, por defecto,
versin. (2)
name: El nombre
(3)
type (optativo, por defecto, integer): El tipo del nmero de
versin.
(4) long , integer , short , timestamp o calendar . de la Los nmeros de versin pueden ser deproperty los tipos de Hibernate access (optativo, por defecto, ): La estrategia que Hibernate debera usar para obtener el valor propiedad.
57 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Una propiedad de versin o timestamp nunca debera ser nula para una instancia desprendida, de manera que Hibernate pueda identificar cualquier instancia con una versin o timestamp nulos como transitoria, sin importar qu otras estrategias de unsaved-value se hayan usado. Declarar una propiedad versin o timestamp como anulable es un forma fcil de evitar problemas con la revinculacin transitiva en Hibernate, especialmente til para quienes usen identificadores asignados o claves compuestas!
(1)
column (optativo, por defecto,
(2)
name: El nombre
de una propiedad estilo JavaBeans del tipo Date o Timestamp de la clase persistente.
(3)
access (optativo, por defecto, property): La
propiedad. (4) por defecto, null): Una propiedad de versin, que indica que una instancia ha sido recientemente instanciada (no grabada, "transitoria"), distinguindola de una instancia desprendida, que haya sido cargada o grabada por una sesin previa (undefined especifica que debera usarse el valor de la propiedad indentificadora). (5)
source (optativo, por defecto, vm): De dnde debera Hibernate obtener el valor de la timestamp? De la base de datos, o de la JVM actual? Las timestamps originadas en la base de datos son costosas, porque Hibernate debe consultar la base de datos para determinar el "prximo" valor, pero sern ms seguras en entornos de cluster. Note tambin, que no se sabe si todos los dialectos soportan esta obtencin de timestamps de la base de datos, mientras que otros pueden ser inseguros de usar en "locking", dada su falta de precisin (Oracle 8, por ejemplo). unsaved-value (optativo,
(6)
generated
(optativo, por defecto, never): Especifica que esta propiedad timestamp en realidad es generada por la base de datos. Vea la discusin de propiedades generadas.
Note que <timestamp> equivale a <version type="timestamp">. Y <timestamp source="db"> equivale a <version
type="dbtimestamp">
5.1.11. property
El elemento <property> declara una propiedad pesistente de la clase, estilo JavaBeans.
<property name="propertyName" column="column_name" type="typename"
58 de 198
http://www.hibernar.org/documentacion_es/castellano.html
update="true|false" insert="true|false" formula="arbitrary SQL expression" access="field|property|ClassName" lazy="true|false" unique="true|false" not-null="true|false" optimistic-lock="true|false" generated="never|insert|always" node="element-name|@attribute-name|element/@attribute|." index="index_name" unique_key="unique_key_id" length="L" precision="P" scale="S" />
(1)
name: el nombre
(2)
column (optativo, por defecto, the property name): el nombre de la columna de la tabla en la base de datos mapeada. Tambin puede especificarse con elemento(s) <column> anidados.
(3)
type (optativo): un nombre
(4)
update, insert (optativo, por defecto, true) : indica que las columnas mapeadas deben ser incluidas en los comandos SQL UPDATE y/o INSERT. Asignarles false a las dos permite una propiedad puramente "derivada",
cuyo
valor es inicializado solamente por alguna otra propieadad que se mapea a la(s) misma(s) columna(s), o por un trigger, u otra aplicacin. (5)
formula (optativo): una expresin SQL que define el valor de una propiedad computada. Las propiedades computadas no tienen un mapeo de columna propio.
(6)
access (optativo, por defecto, property): La
propiedad. (7)
lazy (optativo, por defecto, false): Especifica que esta propiedad debera ser obtenida de manera haragana, cuando se acceda por primera vez a la variable de instancia (requiere implementacin bytecode en tiempo de ejecucin).
(8)
unique (optativo): Habilita la generacin del lenguaje de definicin de datos (DDL) para una constraint nica para las columnas. Tambin permite que esto sea apuntado por una property-ref.
(9)
not-null (optativo): Habilita la generacin del lenguaje de definicin de datos (DDL) para una constraint de nulabilidad para las columnas.
(10)
optimistic-lock (optativo, por
defecto, true): Especifica si las actualizaciones al valor de esta propiedad requieren o no un "lock" optimista. En otras palabras, determina si pueden ocurrir incrementos de versin cuando la propiedad est "sucia".
(11)
generated
(optativo, por defecto, never): Especifica que el valor de esta propiedad es en realidad generado por la
59 de 198
http://www.hibernar.org/documentacion_es/castellano.html
typename puede ser: El nombre de un tipo bsico de Hibernate (por ejemplo integer, string, character, date, timestamp, float, binary, serializable, object, blob). El nombre de una clase de Java con un tipo bsico por defecto (por ejemplo int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob). El nombre de una clase de Java serializable. El nombre de un tipo de Java hecho a medida (por ejemplo, com.mipaquete.MiTipoPersonalizado). Si no se especifica un tipo, Hibernate usar reflexin en la propiedad nombrada, para adivinar el tipo Hibernate correcto. Hibernate intentar interpretar el nombre de la clase devuelta por el mtodo "getter" de la propiedad, usando las reglas 2, 3 y 4, en ese orden. De todos modos, esto no siempre es suficiente. En algunos casos, an se necesita el atributo type (por ejemplo, para distinguir entre Hibernate.DATE e Hibernate.TIMESTAMP, o para especificar un tipo hecho a medida). El atributo access le permite controlar cmo Hibernate acceder a la propiedad en tiempo de ejecucin. Por defecto, Hibernate invocar al par get/set. Si usted especifica access="field", Hibernate saltear el par get/set y acceder al campo directamente, usando reflexin. Usted puede especificar su propia estrategia de acceso, nombrando una clase que implemente la interfaz org.hibernate.property.PropertyAccessor. Las propiedades derivadas son una caracterstica especialmente poderosa. Estas propiedades son de slo lectura, por definicin. Son computadas en el momento en que se cargan. Se declara dicha computacin como un comando SQL, que se traduce en una subconsulta SELECT en el cdigo SQL que carga la instancia.
<property name="totalPrice" formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p WHERE li.productId = p.productId AND li.customerId = customerId AND li.orderNumber = orderNumber )"/>
Note que usted se puede referir a la tabla misma de la entidad, evitando usar el alias para una columna en particular (en el ejemplo, customerId). Tambin note que puede usar elementos <formula> anidados si no le gusta especificar el valor como un atributo.
5.1.12. many-to-one
Una asociacin comn con otra clase persistente se declara usando un elemento many-to-one element. El modelo relacional es una asociacin de-muchos-a-uno: la clave fornea en una tabla se refiera a la(s) columna(s) clave de la tabla de destino.
<many-to-one name="propertyName" column="column_name" class="ClassName" cascade="cascade_style" fetch="join|select" update="true|false" insert="true|false" property-ref="propertyNameFromAssociatedClass" access="field|property|ClassName" unique="true|false" not-null="true|false" optimistic-lock="true|false" lazy="proxy|no-proxy|false" not-found="ignore|exception" entity-name="EntityName" formula="arbitrary SQL expression" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" index="index_name"
(1) (2) (3) (4) (5) (6) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15)
60 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(1)
name: El nombre
de la propiedad.
(2)
column (optativo): El nombre <column> anidados.
(3)
class (optativo, por defecto,
(4)
cascade (optativo): Especifica qu operaciones sern propagadas en cascada desde el objeto padre hacia los objetos asociados.
(5)
fetch (optativo, por defecto, select): Elije
secuencial. (6)
update, insert (optativo, por defecto, true) especifica que las columnas mapeadas deben incluirse en el los comandos SQL UPDATE y/o INSERT. Asignarles false permite una propiedad puramente "derivada", cuyo valor es
inicializado desde alguna otra propiedad, o desde un trigger, u otra aplicacin. (7)
property-ref: (optativo) El nombre de una propiedad de una clase asociada que est ligada a esta clave fornea. Si no se especifica, se usa la clave primaria de la clase asociada.
(8)
access (optativo, por defecto, property): La
propiedad. (9)
unique (optativo): Habilita la generacin de lenguaje de definicin de datos (DDL) para la creacin de una constraint nica para la columna de clave fornea. Tambin permite que sta sea el blanco referido por una property-ref. Esto hace que la "multiplicidad" de la asociacin sea, efectivamente, de-uno-a-uno.
(10)
not-null (optativo): Habilita la generacin de lenguaje de definicin de datos (DDL) para la creacin de una constraint de nulabilidad para las columnas de la clave fornea.
(11)
optimistic-lock (optativo, por
defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no la adquisicin de un "lock" optimista. En otras palabras, determina si debera ocurrir un incremento de versin cuando esta propiedad est "sucia".
(12)
lazy (optativo, por defecto, proxy): Las asociaciones de extremo nico estn "proxied" por defecto. lazy="noproxy" especifica que el valor de la propiedad debe ser capturado en forma haragana, cuando se acceda a la variable de instancia por primera vez (requiere instrumentacin bytecode en tiempo de ejecucin). lazy="false"
especifica que la asociacin siempre ser capturada de manera ansiosa ("eager" fetching). (13)
not-found
(optativo, por defecto, exception): Especifica cmo sern manejadas las claves forneas que se refieran a filas ausentes. ignore tratar dicha fila ausente como si fuera una asociacin nula.
61 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(14)
entity-name
(15)
formula
(optativo): una expresin SQL que define el valor de una clave fornea computada.
Asignarle cualquier valor que tenga sentido al atributo cascade que no sea none propagar ciertas operaciones al objeto asociado. Los valores que tienen sentido son las operaciones bsicas de Hibernate, persist, merge, delete, save-update, evict, replicate, lock, refresh, as como los valores especiales delete-orphan y all, y combinaciones de nombres de operacion separados por comas, por ejemploc ascade="persist,merge,evict" o cascade="all,delete-orphan". Vea la Seccin 10.11, Persistencia transitiva para una explicacin completa. Note que las asociaciones a un solo valor (de-muchos-a-uno y de-uno-a-uno) no soportan el borrado de hurfanos. Una declaracin many-to-one tpica se ve tan simple como esto:
<many-to-one name="product" class="Product" column="PRODUCT_ID"/>
El atributo property-ref debe ser usado solamente para datos heredados/anticuados en donde la clave fornea apunte a una clave nica de la tabla asociada que no es la clave primaria. ste es un modelo relacional feo. Por ejemplo, suponga que la clase Product class tiene un nmero de serie nico, que no es la clave primaria. (El atributo unique controla en Hibernate la generacin de DDL con la herramienta SchemaExport).
<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>
De todos modos, esto se desaconseja. Si la clave nica referida comprende mltiples propiedades de la entidad asociada, usted debera mapear las propiedades referidas dentro de un elemento llamado <properties>. Si la clave nica referida es la propiedad de un componente, se puede especificar un "path de propiedades":
<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>
5.1.13. one-to-one
Una asociacin de-uno-a-uno a otra clase persistente se declara usando el elemento one-to-one.
<one-to-one name="propertyName" class="ClassName" cascade="cascade_style" constrained="true|false" fetch="join|select" property-ref="propertyNameFromAssociatedClass" access="field|property|ClassName" formula="any SQL expression" lazy="proxy|no-proxy|false" entity-name="EntityName" node="element-name|@attribute-name|element/@attribute|." embed-xml="true|false" foreign-key="foreign_key_name" />
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10)
(1)
name: El nombre
de la propiedad.
62 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(2)
class (optativo, por defecto,
(3)
cascade (optativo) especifica qu operaciones deben ser propagadas en cascada desde el objeto padre hacia el objeto asociado.
(4)
constrained (optativo) especifica que una constraint de clave fornea a la clave primaria de la tabla mapeada apunta a la tabla de la clase asociada. Esta opcin afecta el orden en que save() y delete() son propagadas en cascada, y determina si la asociacin puede generar "proxies" (tambin es usada por la herramienta de exportacin de esquema de base de datos).
(5)
fetch (optativo, por defecto, select): Elige
(6) El nombre de una propiedad de la clase asociada que est asociada a la clave primaria de esta clase. Si no se especifica, se usa la clave primaria de la clase asociada. (7)
access (optativo, por defecto, property): La property-ref: (optativo)
propiedad. (8)
formula (optativo): Casi todas las asociaciones de-uno-a-uno se mapean a la clave primaria de la entidad a la que pertenecen, En el raro caso de que no sea as, se puede especificar alguna otra columna o expresin con la cual asociarla usando una frmula SQL. (vea org.hibernate.test.onetooneformula por un ejemplo).
(9)
lazy (optativo, por defecto, proxy): Por defecto, las asociaciones de extremo nico usan proxies. lazy="noproxy" especififica que la propuedad debe ser capturada en forma haragana cuando se accede a la variable de instancia por primera vez (requiere instrumentacin bytecode de tiempo de ejecucin). lazy="false" especifica
que asociacin ser siempre capturada de forma "ansiosa" (eager fetching). Note que si se usa constrained="false", usar proxies es imposible e Hibernate siempre capturar en forma ansiosa! (10)
entity-name
Hay dos variantes de asociacin de-uno-a-uno: asociaciones por clave primaria. asociaciones por clave fornea nica. Las asociaciones por clave primaria no necesitan una columna extra en la tabla; si dos filas estn relacionadas por esta asociacin, entonces las dos filas en las respectivas tablas comparten el mismo valor de clave primaria. As que si usted quiere que dos objetos estn relacionados por la misma asociacin de clave primaria, tiene que asegurarse de que se les asigne el mismo valor de identificador! Para una asociacin por clave primaria, agrguele los siguientes mapeos a Employee y Person, respectivamente.
<one-to-one name="person" class="Person"/> <one-to-one name="employee" class="Employee" constrained="true"/>
Ahora, debemos asegurarnos de que las claves primarias de las filas relacionadas en las tablas PERSON y EMPLOYEE son iguales. Usamos una estrategia especial de Hibernate para asegurarnos de que las claves primarias de filas relacionadas sean iguales: un a estrategia de generacin de identificador llamada foreign:
63 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<class name="person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="foreign"> <param name="property">employee</param> </generator> </id> ... <one-to-one name="employee" class="Employee" constrained="true"/> </class>
Entonces, a una instancia recientemente creada de Person se le asigna el mismo valor de clave primaria que a la instancia de Employee referida por la propiedad employee. Alternativamente, una clave fornea que apunte a una constraint nica de Employee a Person, puede expresarse como:
<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>
Y esta asociacin puede ser convertida en bidireccional agregando lo siguiente al mapeo de Person:
<one-to-one name="employee" class="Employee" property-ref="person"/>
5.1.14. natural-id
<natural-id mutable="true|false"/> <property ... /> <many-to-one ... /> ...... </natural-id>
Aunque recomendamos el uso de clave sustitutas como claves primarias, an se deberan identificar claves naturales para todas las entidades. Una clave natural es una propiedad o combinacin de propiedades que sea nica y no nula. Si tambin es inmutable, mejor todava. Mapee las propiedades de la clave natural dentro del elemento <natural-id>, e Hibernate generar las constraints necesarias de unicidad y nulabilidad, y su mapeo quedar ms auto-documentado. Recomentamos fuertemente que implemente equals() y hashCode() para comparar las propiedades de la clave natural de la entidad. Este mapeo no debera usarse con entidades para las cuales la clave primaria es la clave natural.
mutable (optativo,
por defecto, false): Por defecto, propiedades identificador naturales que asume son inmutables
(constantes).
64 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(1)
name: El nombre
de la propiedad
(2)
class (optativo, por defecto,
(4)
update: Aparence
(5)
access (optativo, por defecto, property): La
propiedad. (6)
lazy (optativo, por defecto, false): Especifica que este componente debera ser capturado en forma haragana (lazy fetching) cuando se acceda a la variable de instancia por primera vez (requiere implementacin bytecode en tiempo de ejecucin).
(7)
optimistic-lock (optativo, por
defecto, true): Especifica si las actualizaciones de este componente requieren o no la adquisicin de un "lock" optimista. En otras palabras, determina si debera haber un incremento de versin cuando la propiedad est "sucia"
(8)
unique (optativo, por defecto, false): Especifica
Las propiedades <property> hijas mapean propiedades de la clase hija a columnas de la tabla. El elemento <component> acepta un subelemento <parent> que mapea una propiedad del componente como una referencia que apunta de vuelta a la entidad contenedora. El elemento <dynamic-component> permite que un Map sea mapeado como componente, en donde el nombre de la propiedad se refiere a claves del mapa, vase Seccin 8.5, Componentes dinmicos.
5.1.16. properties
El elemento <properties> permite la definicin de un agrupamiento lgico, con un nombre, de algunas propiedades de una clase. La utilidad ms importante de este tipo de construccin, es que permite que una combinacin de propiedades sea el blanco de un property-ref. Tambin es una manera conveniente de definir una constraint de unicidad.
<properties name="logicalName" insert="true|false" update="true|false" optimistic-lock="true|false" unique="true|false" > <property ...../> <many-to-one .... /> ........ </properties>
(1)
name: El nombre
65 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(2)
insert: Aparecen las propiedades mapeadas en los comandos INSERT?
(3)
update: Aparecen las propiedades mapeadas en los comandos UPDATE?
(4)
optimistic-lock (optativo, por
defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no la adquisicin de un "lock" optimista. En otras palabras, determina si debera ocurrir un incremento de versin cuando esta propiedad est "sucia".
(5)
unique (optativo, por defecto, false): Especifica
Y entonces podemos tener una asociacin de datos al estilo anticuado, que se refiera a esta clave nica de la tabla Person table, en lugar de a la clave primaria.
<many-to-one name="person" class="Person" property-ref="name"> <column name="firstName"/> <column name="initial"/> <column name="lastName"/> </many-to-one>
5.1.17. subclass
Finalmente, la persistencia polimrfica requiere la declaracn de cada subclase de la clase persistente raz. Para la estrategia de mapeo una-tabla-por-clase, se usa la declaracin <subclass>.
<subclass name="ClassName" discriminator-value="discriminator_value" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" entity-name="EntityName" node="element-name" extends="SuperclassName"> <property .... /> ..... </subclass>
(1) Cada subclase debera declara sus propias propiedades persistentes y subclases. Se asume que las propiedades <version> name: El nombre (enteramente calificado) de la subclase. e <id> sern heredadas de la clase raz. Cada subclase en la jerarqua debe definir un valor nico de discriminator-
66 de 198
http://www.hibernar.org/documentacion_es/castellano.html
value. Si no
5.1.18. joined-subclass
Alternativamente, cada subclase puede ser mapeada a su propia tabla (la estrategia de mapeo "una-table-por-subclase"). El estado heredado se captura haciendo un "join" con la tabla de la superclase. Usamos el elemento <joined-subclass>.
<joined-subclass name="ClassName" table="tablename" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <key .... > <property .... /> ..... </joined-subclass>
(1)
name: El nombre
(2)
table: El nombre
de la tabla de la subclase.
(3)
proxy
(optativo): Especifica una clase o interfaz a usar para la inicilizacin haragana de proxies.
(4)
lazy
(optativo, por defecto, true): Asignar lazy="false" inhabilita el uso de captura haragana (lazy fetching).
Para esta estrategia de mapeo no se requiere ninguna columna discriminadora. Sin embargo, cada subclase debe declarar una columna de tabla que contenga al identificador de objeto, usando el elemento <key>. El mapeo del comienzo del captulo sera rescrito as:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="eg"> <class name="Cat" table="CATS"> <id name="id" column="uid" type="long"> <generator class="hilo"/> </id> <property name="birthdate" type="date"/> <property name="color" not-null="true"/> <property name="sex" not-null="true"/> <property name="weight"/> <many-to-one name="mate"/> <set name="kittens"> <key column="MOTHER"/> <one-to-many class="Cat"/> </set> <joined-subclass name="DomesticCat" table="DOMESTIC_CATS"> <key column="CAT"/>
67 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<property name="name" type="string"/> </joined-subclass> </class> <class name="eg.Dog"> <!-- ac podra ir el mapeo de Dog --> </class> </hibernate-mapping>
Para informacin sobre los mapeos de herencias, vea Captulo 9, Mapeo de Herencia.
5.1.19. union-subclass
Una tercera opcin es mapear a tablas slo las clases concretas de la jerarqua de herencias. (la estrategia de "una-tablapor-clase-concreta"), en la cual una tabla define todo el estado persustente de la clase, incluido el estado persistente. En Hibernate, no es absolutamente necesario mapear dichas jerarquas de herencia. Se puede, simplemente, mapear cada clase con una declaracn separada de <class>. De todos modos, si desea usar asociaciones polimrficas (por ejemplo, una asociacin a la superclase de su jeraqua), es necesario usar un mapeo con <union-subclass>.
<union-subclass name="ClassName" table="tablename" proxy="ProxyInterface" lazy="true|false" dynamic-update="true|false" dynamic-insert="true|false" schema="schema" catalog="catalog" extends="SuperclassName" abstract="true|false" persister="ClassName" subselect="SQL expression" entity-name="EntityName" node="element-name"> <property .... /> ..... </union-subclass>
(1)
name: El nombre,
(2)
table: El nombre
de la tabla de la subclase.
(3)
proxy (optativo): Especifica
(4)
lazy (optativo, por defecto, true): Especificar lazy="false" inhabilita
fetching).
Para esta estrategia de mapeo no se requiere una columna discriminadora. Para informacin sobre los mapeos de herencias, vea Captulo 9, Mapeo de Herencia.
5.1.20. join
Usando el elemento <join>, es posible mapear propiedades de una clase a varias tablas, cuando hay una relacion de-uno-a-uno entre dichas tablas.
<join
68 de 198
http://www.hibernar.org/documentacion_es/castellano.html
table="tablename" schema="owner" catalog="catalog" fetch="join|select" inverse="true|false" optional="true|false"> <key ... /> <property ... /> ... </join>
(1)
table: El nombre
de la tabla asociada.
(2)
schema (optativo): Suplanta <hibernate-mapping>.
(3)
catalog (optativo): Suplanta <hibernate-mapping>.
(4) le asigna el valor pr defecto join, Hibernate usar un INNER JOIN para capturar una asociacin definida por una clase en su superclase, y utilizar un OUTER JOIN para una asociacin definida por una subclase. Si se le asigna el valor select, entonces Hibernate usar una asociacin secuencial definida en una subclase, la cual slo ser emitida cuando ocurra que una fila represente una instancia de la subclase. Los INNER JOINS an sern usados para capturar asociaciones definidas por la clase y sus superclases. (5)
inverse fetch (optativo, por defecto, join): Si se
(optativo, por defecto, false): Si se habilita, Hibernate no intentar insertar o acualizar las propiedades definidas por esta asociacin.
(6)
optional
(optativo, por defecto, false): Si se habilita, Hibernate insertar una nueva fila slo si las propiedades definidas por esta asociacin son no nulas, y siempre usar un OUTER JOIN para capturar las propiedades.
Por ejemplo, la informacin de la direccin de una perosna puede ser mapeada en una tabla separada, al tiempo que se preserva la semntica para todas las propiedades.
<class name="Person" table="PERSON"> <id name="id" column="PERSON_ID">...</id> <join table="ADDRESS"> <key column="ADDRESS_ID"/> <property name="address"/> <property name="zip"/> <property name="country"/> </join> ...
Esta caracterstica es a menudo slo til para modelos de datos anticuados. Recomendamos que haya menos tablas que clases, y un modelo de dominio bien detallado. N.del T.: se les suele llamar "detallados" (en ingls "fine-grained") a los modelos con clases que contiengan otras clases como miembro, por ejemplo, una clase Direccin dentro de una clase Persona, en lugar de incluir los campos de la direccn (calle, nmero, etc) directamente en Persona. Aunque los campos de Direccin probablemente se terminen persistiendo en la misma tabla que los de Persona, se considera que una representacin jerrquica que use las clases Persona y Direccin es ms "detallada" que la simple tabla PERSONA subyacente
69 de 198
http://www.hibernar.org/documentacion_es/castellano.html
De todos modos, "join-table" es til para alternar entre distintas estrategias de mapeo en una misma jerarqua, como se explica ms adelante.
5.1.21. key
Hemos visto surgir el elemento <key> varias veces ya. Aparece cada vez que el mapeo de un elemento padre define una asociacin a una nueva tabla, que haga referencia a la clave primaria de la tabla original.
<key column="columnname" on-delete="noaction|cascade" property-ref="propertyName" not-null="true|false" update="true|false" unique="true|false" /> (1) (2) (3) (4) (5) (6)
(1)
column (optativo): El nombre <column> anidados.
de la columna de clave fornea. Esto tambin puede ser especficado por elementos
(2)
on-delete
(optativo, por defecto, noaction): Especifica si la constraint de clave fornea tiene habilidtado el borrado en cascada a niver de la base de datos.
(3)
property-ref (optativo): Especifica que la clave fornea se refiere a columnas que no son la clave primaria de la tabla original. (provisto para datos anticuados/heredados solamente).
(4)
not-null (optativo): Especifica que las columnas de la clave fornea no son anulables (lo cual se sobreentiende cuando la clave fornea es tambin parte de la clave primaria)
(5)
update (optativo): Especifica que la clave fornea nunca debera ser actualizada (lo cual se sobreentiende cuando la clave fornea es tambin parte de la clave primaria)
(6)
unique (optativo): Especifica que la clave fornea debera tener una constraint de unicidad (lo cual se sobreentiende cuando la clave fornea es tambin la clave primaria).
Recomendamos que, para sistemas en los cuales la performance de delete sea importante, toda las claves sean definidas con on-delete="cascade", e Hibernate generar una constraint ON CASCADE DELETE a nivel de la base de datos, en lugar de varios comandos DELETE individuales. Tenga presente que esta caracterstica saltea la estrategia usual de locking optimista para datos versionados. Los atributos not-null y update son tiles cuando se mapea una asociacin de-uno-a-muchos unidireccional. Si la mapea a una clave fornea no anulable, hay que declarar la columa clave usando <key not-null="true">.
70 de 198
http://www.hibernar.org/documentacion_es/castellano.html
unique="true|false" unique-key="multicolumn_unique_key_name" index="index_name" sql-type="sql_type_name" check="SQL expression" default="SQL expression"/> <formula>SQL expression</formula>
Los atributos column y formula pueden incluso ser combinados dentro de la mismo mapeo de propiedad o asociacin, para expresar, por ejemplo, codiciones de asociacin exticas.
<many-to-one name="homeAddress" class="Address" insert="false" update="false"> <column name="person_id" not-null="true" length="10"/> <formula>'MAILING'</formula> </many-to-one>
5.1.23. import
Supongamos que su aplicacin tiene dos clases persistenes con el mismo nombre, y usted no quiere especificar un nombre de clase enteramente calificado (con el paquete) en las consutas de Hibernate. Las clases pueden ser "importadas" explcitamente en lugar de tener que confiar en el auto-import="true". Usted puede incluso importar clases e interfaces que no estn mapeadas explcitamente.
<import class="java.lang.Object" rename="Universe"/> <import class="ClassName" rename="ShortName" /> (1) (2)
(1)
class: The
(2)
rename (optativo,
por defecto, the unqualified class name): A name that may be used in the query language.
5.1.24. any
Hay un tipo ms de mapeo de propiedad: El elemento de mapeo <any> define una asociacin polimrfica a clases de mltiples tablas. Este tipo de mapeo siempre requiere ms de una columna. La primera columna contiene el tipo de la entidad asociada. Es imposible especificar una constraint de clave fornea para este tipo de asociaciones. Esto se debera usar slo en casos muy especiales (por ejemplo, logs de auditora, datos de sesin de usuario, etc). El atributo meta-type le permite a la aplicacin especificar un tipo hecho a medida, que mapear valores de columna en la base de datos a clases persistentes, las cuales tendrn propiedades indentificadoras del tipo identificado por id-type. Se debe especificar el mapeo desde los valores valores del meta-tipo a los nombres de las clases.
<any name="being" id-type="long" meta-type="string"> <meta-value value="TBL_ANIMAL" class="Animal"/> <meta-value value="TBL_HUMAN" class="Human"/> <meta-value value="TBL_ALIEN" class="Alien"/> <column name="table_name"/> <column name="id"/> </any> <any name="propertyName" id-type="idtypename" meta-type="metatypename" cascade="cascade_style" access="field|property|ClassName" (1) (2) (3) (4) (5)
71 de 198
http://www.hibernar.org/documentacion_es/castellano.html
optimistic-lock="true|false" > <meta-value ... /> <meta-value ... /> ..... <column .... /> <column .... /> ..... </any>
(6)
(1)
name: el nombre
de la propiedad.
(2)
id-type: el tipo del identificador.
(3)
meta-type
(optativo, por defecto, string): Cualquier tipo que sea permisible para mapear un discriminador.
(4)
cascade
(5)
access (optativo, por defecto, property): La
propiedad. (6)
optimistic-lock (optativo, por
defecto, true): Especifica si las actualizaciones de esta propiedad requieren o no la adquisicin de un "lock" optimista. En otras palabras, determina si debera ocurrir un incremento de versin cuando esta propiedad est "sucia".
72 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Retomaremos estos conceptos todo a lo largo de la documentacin. El desafo es mapear el sistema de tipos de Java (junto con la definicin del desarrollador de las entidades y "value types") a un sistema del tipo SQL/BD. El puente entre ambos sistemas es provisto por Hibernate. Para las entidades proveemos <class>, <subclass> y as sucesivamente. Para los "value types" usamos <property>, <component>, etc., normalmente con un atributo type. El valor de este atributo es uno de los tipos para mapeo de Hibernate. Hibernate trae varios mapeos (para los tipos estndar de la JDK) ya incluidos. Pero usted puede escribir sus propios tipos para mapeo e implementar sus propias estrategias de conversin, como veremos ms adelante. Todos los tipos que vienen ya incluidos en Hibernate, excepto las colecciones, soportan la semntica de nulo.
Mapeos de los respectivos tipos primitivos o "clases envoltorio" a valores de columna SQL (que dependen de la marca de la BD): boolean, yes_no y true_false son todas codificaciones alternativas de un boolean o un java.lang.Boolean de Java.
string
Mapeos de tipo de java.util.Date y sus subclases a tipos SQL DATE, TIME y TIMESTAMP (o equivalentes).
calendar, calendar_date
Mapeos de tipo de java.util.Locale, java.util.TimeZone y java.util.Currency a VARCHAR (o el VARCHAR2 de Oracle). Las instancias de Locale y Currency son mapeadas a sus cdigos ISO. Las instancias de TimeZone son mapeadas a su ID.
class
Un mapeo de tipo java.lang.Class a VARCHAR (o la VARCHAR2 de Oracle). Una Class es mapeada con su nombre enteramente calificado.
binary
Mapea tipos serializables de Java a un tipo SQL binario apropiado. Tambin se puede indicar el tipo de Hibernate serializable con el nombre de una clase o interfaz serializable de Java, que no sea por defecto un tipo bsico.
clob, blob
Mapeos de tipo para las clases JDBC java.sql.Clob y java.sql.Blob. Estos tipos pueden ser inconvenientes para algunas aplicaciones, dado que los objetos clob y blob no pueden ser reusados fuera de una transaccin. (Ms
73 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Mapeos de tipo para lo que normalmente se considera "tipos mutables de Java", en los que Hibernate adopta ciertas optimizaciones que son slo apropiadas para tipos inmutables de Java, y la aplicacin trata al objeto como inmutable. Por ejemplo: para una instancia mapeada como imm_timestamp, no se debera invocar Date.setTime(). Para cambiar el valor de la propiedad (y hacer que ese valor cambiado sea persistido), la aplicacin tiene que asignarle un nuevo objeto (no idntico) a la propiedad.
clob.
Los identificadores nicos de las entidades y colecciones pueden ser de cualquier tipo bsico excepto binary, blob y (Los identiicadores compuestos tambin estn permitidos, ver ms adelante).
Los "value types" bsicos tienen constantes Type definidas en org.hibernate.Hibernate. Por ejemplo, Hibernate.STRING representa el tipo string.
Note el uso de los elementos <column> para mapear una sola propiedad a mltiples columnas. Las interfaces CompositeUserType, EnhancedUserType, UserCollectionType, y UserVersionType poveen soporte para usuarios ms especializados. Incluso se le pueden proveer parmetros a un UserType en el archivo de mapeo. Para lograr esto, su UserType debe implementar la interfaz org.hibernate.usertype.ParameterizedType. Para proveerle parmetros a su tipo a medida, usted puede usar el elemento <type> en sus archivos de mapeo.
<property name="priority"> <type name="com.mycompany.usertypes.DefaultValueIntegerType"> <param name="default">0</param> </type> </property>
Ahora el UserType puede aceptar valrores para el parmetro llamado default con el objeto Properties que le fue pasado. Si se usa un objeto UserType muy a menudo, sera til definirle un nombre corto. Esto se puede hacer usando el elemento <typedef>. Los "typedefs" le asignan un nombre a un tipo a medida, y pueden contener tambin una lista de parmetros por defecto si el tipo es parametrizado.
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero"> <param name="default">0</param> </typedef> <property name="priority" type="default_zero"/>
74 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Tambin es posible sustituir (override) los parmetros provistos en un typedef, caso por caso, usando parmetros de tipo en el mapeo de propiedades. Aunque la rica variedad de tipos ya incluidos en Hibernate hace que slo en contadas ocasiones realmente se necesite usar un tipo a medida, se considera aconsejable crear tipos a medida para clases (no entidades) que ocurran frecuentemente en su aplicacin. Por ejemplo, una clase MonetaryAmount (suma de dinero) sera una buena candidata para un CompositeUserType, incluso si pudiere ser fcilmente mapeada como componente. Uno de los motivos para esto es abstraccin. Con un tipo a medida como ste, sus documentos de mapeo sern a prueba de posibles cambios en la forma en que los valores monetarios se representaren en el futuro.
Note cmo ahora las asociaciones son especificadas usando entity-name en lugar de class.
75 de 198
http://www.hibernar.org/documentacion_es/castellano.html
* table="CATS" */ public class Cat { private Long id; // identifier private Date birthdate; private Cat mother; private Set kittens private Color color; private char sex; private float weight; /* * @hibernate.id * generator-class="native" * column="CAT_ID" */ public Long getId() { return id; } private void setId(Long id) { this.id=id; } /** * @hibernate.many-to-one * column="PARENT_ID" */ public Cat getMother() { return mother; } void setMother(Cat mother) { this.mother = mother; } /** * @hibernate.property * column="BIRTH_DATE" */ public Date getBirthdate() { return birthdate; } void setBirthdate(Date date) { birthdate = date; } /** * @hibernate.property * column="WEIGHT" */ public float getWeight() { return weight; } void setWeight(float weight) { this.weight = weight; } /** * @hibernate.property * column="COLOR" * not-null="true" */ public Color getColor() { return color; } void setColor(Color color) { this.color = color; } /** * @hibernate.set * inverse="true" * order-by="BIRTH_DATE" * @hibernate.collection-key * column="PARENT_ID" * @hibernate.collection-one-to-many */ public Set getKittens() { return kittens; } void setKittens(Set kittens) { this.kittens = kittens; }
76 de 198
http://www.hibernar.org/documentacion_es/castellano.html
// addKitten not needed by Hibernate public void addKitten(Cat kitten) { kittens.add(kitten); } /** * @hibernate.property * column="SEX" * not-null="true" * update="false" */ public char getSex() { return sex; } void setSex(char sex) { this.sex=sex; } }
Note que el soporte para anotaciones JDK 5.0 (y JSR-220) todava es un trabajo en curso, incompleto. Por favor remtase al mdulo de Anotaciones de Hobernate para ms detales.
77 de 198
http://www.hibernar.org/documentacion_es/castellano.html
significa que el valor de la propiedad dada nunca es generada por la base de datos.
que el valor de la propiedad dada es generado al ocurrir un INSERT, pero no es regenerado en los UPDATEs subsiguientes. Cosas como "fecha de creacin" entraran en esta categora. Note que, aunque las propiedades version y timestamp pueden ser marcadas como generadas, esta opcin no est disponible para ellas.
always - declara
La segunda es proveer una clase a medida, la cual sepa cmo construir los comandos CREATE y DROP. Esta clase a medida debe implementar la interfaz org.hibernate.mapping.AuxiliaryDatabaseObject.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> </database-object> </hibernate-mapping>
Adicionalmente, a estos objetos de base de datos se les puede definir un alcance, de manera que se apliquen slo cuando se usen ciertos dialectos determninados.
<hibernate-mapping> ... <database-object> <definition class="MyTriggerDefinition"/> <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/> <dialect-scope name="org.hibernate.dialect.OracleDialect"/> </database-object> </hibernate-mapping>
78 de 198
http://www.hibernar.org/documentacion_es/castellano.html
public String getSerialNumber() { return serialNumber; } void setSerialNumber(String sn) { serialNumber = sn; } }
La interfaz puede ser, efectivamente, un java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap o cualquier otra cosa que implemente la interfaz org.hibernate.usertype.UserCollectionType.) Dese cuenta de cmo inicializamos la variable de instancia con una instancia de HashSet. sta es la mejor manera de inicializar propiedades con valor de coleccin, o instancias recientemente inicializadas (no persistentes). Cuando una clase se vuelve persistente (al llamar persist(), por ejemplo) Hibernate en realidad reemplazar el HashSet con una implementacin de Set propia de Hibernate mismo. Est atento a errores como stos:
Cat cat = new DomesticCat(); Cat kitten = new DomesticCat(); .... Set kittens = new HashSet(); //kittens=gatitos kittens.add(kitten); cat.setKittens(kittens); session.persist(cat); kittens = cat.getKittens(); // Correcto, porque la coleccin kittens es un Set (HashSet) cat.getKittens(); // Error!
Las colecciones persistentes que son inyectadas por Hibernate se comportan como: HashMap, HashSet, TreeMap, TreeSet or ArrayList, dependiendo del tipo de interfaz. Las instancias de coleccioones tienen el comportamiento usual de los "value tupes". Son automticamente persistidas cuando son referidas por un objeto persistente, y aumticamente borradas cuando son "des-referidas". Si una coleccin se pasa de un objeto persistente a otro, sus elementos pueden ser movidos de una tabla a otra. Dos entidades no pueden compartir una referencia a la misma instancia de una coleccin. Dado el modelo relacional subyacente, las propiedades con valor de coleccin no soportan la semntica de nulo. Hibernate no distingue entre una coleccin nula y una coleccin vaca. No vale la pena preocuparse mucho por nada de esto. Use las colecciones persistentes del mismo modo en que usara las colecciones comunes de Java. Slo asegrese de comprender la semntica de las asociaciones bidireccionales (la cual se discute ms adelante).
Adems de <set>, tambin estn los elementos de mapeo <list>, <map>, <bag>, <array> y <primitive-array> . El elemento <map> es representativo:
<map name="propertyName" table="table_name" schema="schema_name" lazy="true|extra|false"
79 de 198
http://www.hibernar.org/documentacion_es/castellano.html
inverse="true|false"
(5)
cascade="all|none|save-update|delete|all-delete-orphan|delet(6)e-orphan" sort="unsorted|natural|comparatorClass" (7) order-by="column_name asc|desc" (8) where="arbitrary sql where condition" (9) fetch="join|select|subselect" (10) batch-size="N" access="field|property|ClassName" optimistic-lock="true|false" mutable="true|false" node="element-name|." embed-xml="true|false" > <key .... /> <map-key .... /> <element .... /> </map> (11) (12) (13) (14)
(1)
name el nombre
de la propiedad coleccin
(2)
table (optativo, por defecto, el nombre
raz. (4)
lazy (optativo, por defecto, true) puede usarse para inhabilitar la "captura haragana" (lazy fetching) y especificar que la asociacin es siempre capturada en forma "ansiosa" (eager fetching), o para especificar que la asociacin es "sper-haragana", en donde la mayora de las operaciones evitan inicializar la coleccin (prescrito para colecciones muy grandes).
(5)
inverse
(optativo, por defecto, false) marca esta coleccin como el extremo "inverso" de una asociacin bidireccional.
(6)
cascade
(optativo, por defecto, none) les permite a las operaciones propagarse en cascada a las entidades hijas.
(7)
sort (optativo)
especifica una coleccin ordenada con un orden natural, o una clase "comparator" dada.
(8)
order-by (optativo, a partir de JDK1.4 solamente) especifica una columna o columnas de tabla que define el orden de iteracin del Map, Set o bag, junto con un asc o desc opcional (ascendente/descendente).
(9)
where (optativo)
especifica una condicin SQL WHERE arbitraria, a ser usada ciando se capture o borre la coleccin (til cuando la coleccin slo debe contener un subconjunto de los datos disponibles).
(10)
fetch (optativo, por defecto, select)
secuencial. (11) (optativo, porcolecciones defecto, 1) especifica el "tamao de lote" al capturar instancias de esta coleccin en 6.2.1. batch-size Claves forneas de las forma haragana.
80 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Las instancias de colecciones se distinguen en la BD por la clave fornea que posee a la coleccin. Esta clave fornea se denomina columna (o columnas) clave de la coleccin en la tabla de la coleccin. La columna clave de la coleccin es mapeada por el elemento <key>. Puede haber constraints de nulabilidad en la columna de la clave fornea. Para la mayora de las colecciones, esto est implcito. Para las asociaciones de-uno-a-muchos unidireccionales, la columna de la clave primaria es anulable por defecto, as que a veces es necesario especificar not-null="true".
<key column="productSerialNumber" not-null="true"/>
Vea el captulo correspondiente para una definicin completa del elemento <key>.
(1)
(1)
column_name (obligatorio): El nombre
(1)
base
(optativo, por defecto, 0): El valor de columna ndice correspondiente al primer elemento de la lista o array.
(1)
column (optativo): El nombre
(2)
81 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(2)(3)
(1)
column (optativo): El nombre
(2)
formula (optativo): Una
frmula SQL usada para evaluar la clave fornea de la clave del mapa.
(3)
class (obligatorio): La
Si su tabla no tiene una columna ndice, pero usted an desea usar una List como el tipo de propiedad, usted debera mapear la propiedad como una <bag> de Hibernate. Una bag (bolsa) no retiene su orden cuando es obtenida de la base de datos, pero puede ser opcionalmente ordenada.
(1)
column (optativo): El nombre
(2)
formula (optativo): Una
(3)
type (obligatorio): El tipo de
elemento de coleccin
82 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(1)
column (optativo): el nombre
(2)
formula
(optativo): una frmula SQL usada para evaluar el valor del elemento clave fornea
(3)
class (obligatorio): el nombre
de la clase asociada.
(4)
fetch (optativo, por defecto, join): permite capturas para esta asociacin mediante un outer-join o un select secuencial. ste es un caso especial: para una captura total y "ansiosa" (usando un solo SELECT) de una entidad y sus relaciones de-muchos-a-muchos con otras entidades, se debera habilitar la captura (fetch) de tipo join no solamente en la coleccin misma, sino tambin en este atributo del elemento anidado <many-to-many>.
(5)
unique (optativo): habilita la generacin de lenguaje de definicin de datos (DDL) para una constraint de unicidad en la columna de clave fornea. Esto vuelve efectivamente "de-uno-a-muchos" la multiplicidad de la asociacin.
(6)
not-found (optativo, por defecto, exception): Especifica cmo se fornea. ignore tratar la fila faltante como una asociacin nula
(7)
entity-name
(8)
property-ref: (optativo) el nombre de una propiedad, en la clase asociada que est ligada a esta clave fornea. Si no se especifica, se usa la clave primaria de la clase asociada.
Una bag conteniendo enteros, con un orden de iteracin determinado por el atributo order-by:
<bag name="sizes" table="item_sizes" order-by="size asc"> <key column="item_id"/> <element column="size" type="integer"/> </bag>
83 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<list name="carComponents" table="CarComponents"> <key column="carId"/> <list-index column="sortOrder"/> <composite-element class="CarComponent"> <property name="price"/> <property name="type"/> <property name="serialNumber" column="serialNum"/> </composite-element> </list>
(1)
class (obligatorio): El nombre
de la clase asociada.
(2)
not-found
(optativo, por defecto, exception): especifica cmo se manejarn los identificadores en cach que estn refirindose a columnas perdidas. ignore tratar la fila ausente como una asociacin nula.
(3)
entity-name
Note que el elemento <one-to-many> no necesita declarar ninguna columna. Tampoco es necesatio especificar un nombre de tabla en ningn lado. Nota muy importante: Si la columna de clave fornea de una asociacin <one-to-many> se declara NOT NULL, se debe declarar el mapeo de <key> not-null="true" o usar una asociacin bidireccional con el mapeo de la coleccin marcado como inverse="true". Vea la discusn sobre asociaciones bidireccionales ms adelante en este captulo. Este ejemplo muestra un mapa de entidades Part por nombre (donde el nombre de la parte, partName, es una propiedad persistente de Part). Note el uso de un ndice basado en una frmula.
<map name="parts" cascade="all"> <key column="productId" not-null="true"/> <map-key formula="partName"/> <one-to-many class="Part"/> </map>
84 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" sort="my.custom.HolidayComparator"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date" type="date"/> </map>
Valores permitidos del atributo sort son unsorted, natural y el nombre de una clase que implemente java.util.Comparator. Las colecciones ordenadas en realidad se comportan como un java.util.TreeSet o java.util.TreeMap. Si quiere que la base de datos misma ordene los elementos de la coleccin, use el atributo order-by del mapeo de los mapeos de set, bag o map. Esta solucin slo se encuentra disponible bajo la JDK 1.4 o superior (se implementa usando LinkedHashSet o LinkedHashMap). Esto efecta el ordenamiento en la consulta SQL, no en memoria.
<set name="aliases" table="person_aliases" order-by="lower(name) asc"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" order-by="hol_date, hol_name"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
Note que el valor del atributo order-by es un ordenamiento SQL, no HQL! Las asociaciones pueden incluso ser ordenadas por algn criterio arbitrario en tiempo de ejecucin, usandp un filtro (filter()) de coleccin.
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
85 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<id name="id" column="ITEM_ID"/> ... <!-- lado inverso --> <bag name="categories" table="CATEGORY_ITEM" inverse="true"> <key column="ITEM_ID"/> <many-to-many class="Category" column="CATEGORY_ID"/> </bag> </class>
Los cambios que se le hacen slo al extremo inverso de la asociacin, no son persistidos. Esto significa que Hibernate tiene dos representaciones en memoria por cada asociacin bidireccional.: un vnculo de A a B, y otro vnculo de B a A. Esto es ms fcil de entender si se piensa en el modelo de objetos Java y cmo se crea una relacin de-muchos-a-muchos en Java.
category.getItems().add(item); item.getCategories().add(category); session.persist(item); session.persist(category); // La categora ahora "sabe" acerca de la relacin // El tem ahora "sabe" acerca de la relacin // La relacin no ser grabada! // La relacin s ser grabada
El lado no-inverso se usa para grabar en la base de datos la representacin en memoria. Se puede definir una asociacin bidireccional de-muchos-a-muchos mapeando un a asocacin de-uno-a-muchos a la(s) misma(s) columna(s) de la tabla que una asociacin de-muchos-a-uno, y poniendo inverse="true" en el el extremo 'muchos'.
<class name="Parent"> <id name="id" column="parent_id"/> .... <set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
Mapear uno de los extremos de la asociacin con inverse="true" no afecta las propagaciones en cascada. Son conceptos ortogonales!
86 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Pero si tal propiedad no existe en la clase hoja, no podemos concebir la asociacin como verdaderamete bidireccional (hay informacin disponible en un extremo que no lo est en el otro extremo). En este caso, no podemos mapear la coleccin con inverse="true". En lugar de eso, podemos usar el siguiente mapeo.
<class name="Parent"> <id name="id" column="parent_id"/> .... <map name="children"> <key column="parent_id" not-null="true"/> <map-key column="name" type="string"/> <one-to-many class="Child"/> </map> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" insert="false" update="false" not</class>
Note que en este mapeo, el extremo con la coleccin es el responsable de las actualizaciones de la clave fornea. A HACER: esto resulta realmente en la ejecucin de UPDATES innecesarios?
Un segundo abordaje es simplemente remodelar la asociacin como una clase de entidad. ste es el enfoque que se usa ms comnmente. Una ltima alternativa, es usar elementos compuestos, lo cual se discute ms adelante.
87 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Como se puede ver, una <idbag> tiene un generador de id "sinttico", igual que una clase de entidad! A cada fila de la coleccin se le asigna una clave sustituta diferente. Si embargo, Hibernate no provee ningn mecanismo para descubrir el valor de la clave sustituta de una fila en particular. Note que la performance al actualizar una <idbag> es mucho mejor que la de una <bag> comn! Hibernate puede localizar filas individuales eficientemente, y actualizarlas o borraras individualmente, como si fuera una list, un map o un set. En la implementacin actual, el identificador native como estrategia de generacin de identificadores no se soporta para las <idbag>s.
Tiene una coleccin de instancias Child. Si cada child (hijo) tiene como mucho un padre, el mapeo ms natural es una asociacin de-uno-a-muchos:
<hibernate-mapping> <class name="Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
88 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class> </hibernate-mapping>
Alternativamente, si usted insiste absolutamente en que la asociacin debe ser unidireccional, debe declarar la constraint NOT NULL en el mapeo de la <key>.
<hibernate-mapping> <class name="Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children"> <key column="parent_id" not-null="true"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
Por otra parte, si un hijo tiene mltiles padres, lo apropiado es una relacin de-muchos-a-muchos.
<hibernate-mapping> <class name="Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children" table="childset"> <key column="parent_id"/> <many-to-many class="Child" column="child_id"/> </set> </class> <class name="Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
Definiciones de tablas:
create table parent ( id bigint not null primary key )
89 de 198
http://www.hibernar.org/documentacion_es/castellano.html
create table child ( id bigint not null primary key, name varchar(255) ) create table childset ( parent_id bigint not null, child_id bigint not null, primary key ( parent_i alter table childset add constraint childsetfk0 (parent_id) references parent alter table childset add constraint childsetfk1 (child_id) references child
Para ms ejemplos, y una explicacin paso a paso del mapeo de una relacin padre-hijo, vea Captulo 21, Ejemplo: Padre/Hijo. Son posibles mapeos de asociaciones an ms exticas. Catalogaremos todas las posibilidades en el captulo siguiente.
7.2.2. de-uno-a-uno
Una asociacin unidireccional de-uno-a-uno por clave fornea es prcitcamente idntica. La nica diferencia es la constraint de unicidad.
<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> <class name="Address"> <id name="id" column="addressId">
90 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
Una asociacin unidirectional de-uno-a-uno por clave primaria normalmente usa un generador de id especial (note que hemos revertido la direccin de la asociacin en este ejemplo).
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class> create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )
7.2.3. de-uno-a-muchos
Una asociacin unidireccional de-uno-a-muchos por clave fornea es un caso muy inusual, que no recomendamos usar.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <set name="addresses"> <key column="personId" not-null="true"/> <one-to-many class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table Address ( addressId bigint not null primary key, personId bigint not null )
Pensamos que para este caso es mejor usar una tabla de asociacin.
91 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<set name="addresses" table="PersonAddress"> <key column="personId"/> <many-to-many column="addressId" unique="true" class="Address"/> </set> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.3.2. de-muchos-a-uno
Una asociacin A unidireccional de-muchos-a-uno por tabla de asociacin es muy comn, cuando la asociacin es optativa
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null ) create table Address ( addressId bigint not null primary key )
7.3.3. de-uno-a-uno
Una asociacin unidireccional de-uno-a-uno por tabla de asociacin es muy inusual, pero posible.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique create table Address ( addressId bigint not null primary key )
7.3.4. de-muchos-a-muchos
92 de 198
http://www.hibernar.org/documentacion_es/castellano.html
not-null="true"/>
Si se usa una List (u otra coleccin idexada) se necesitar asignarle not null a la columna "key" de la clave fornea, y dejar que Hibernate maneje la asociacin desde el extremo "muchos" para mantener el ndice de cada elemento (convirtiendo el otro lado virtualmente en inverso, al especificar update="false" y insert="false"):
<class name="Person"> <id name="id"/> ... <many-to-one name="address" column="addressId" not-null="true" insert="false" update="false"/> </class> <class name="Address"> <id name="id"/> ... <list name="people"> <key column="addressId" not-null="true"/> <list-index column="peopleIdx"/> <one-to-many class="Person"/> </list> </class>
93 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Es importante que se defina not-null="true" en el elemento <key> del mapeo de la coleccin, si la columna de clave fornea sunyacente es NOT NULL. No debe declararse solamente not-null="true" en un posible elemento <column> anidado, sino en el elemento <key>.
7.4.2. de-uno-a-uno
Un asociacin bidireccional de-uno-a-uno por clave fornea es bastante comn
<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> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class> create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
94 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</id> <join table="PersonAddress" inverse="true" optional="true"> <key column="addressId"/> <many-to-one name="person" column="personId" not-null="true"/> </join> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null primary key ) create table Address ( addressId bigint not null primary key )
7.5.2. de-uno-a-uno
Un asociacin bidireccional de-uno-a-uno por tabla de asociacin es extremadamente inusual, pero posible.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true" inverse="true"> <key column="addressId" unique="true"/> <many-to-one name="person" column="personId" not-null="true" unique="true"/> </join> </class> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique create table Address ( addressId bigint not null primary key )
7.5.3. de-muchos-a-muchos
Finalmente, tenemos una asociacin bidireccional de-muchos-a-muchos.
<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> <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> create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (pers create table Address ( addressId bigint not null primary key )
http://www.hibernar.org/documentacion_es/castellano.html
Asociaciones ms complejas son extremadamente raras. Hibernate posibilita manejar asociaciones ms complejas usando fragmentos SQL incrustados en el documento de mapeo. Por ejemplo, si una tabla con informacin contable histrica definiere las columnas accountNumber, effectiveEndDate and effectiveStartDate, mapeadas de esta manera:
<properties name="currentAccountKey"> <property name="accountNumber" type="string" not-null="true"/> <property name="currentAccount" type="boolean"> <formula>case when effectiveEndDate is null then 1 else 0 end</formula> </property> </properties> <property name="effectiveEndDate" type="date"/> <property name="effectiveStateDate" type="date" not-null="true"/>
entonces podemos mapear una asociacin a la instancia actual (la que tiene effectiveEndDate nula) usando:
<many-to-one name="currentAccountInfo" property-ref="currentAccountKey" class="AccountInfo"> <column name="accountNumber"/> <formula>'1'</formula> </many-to-one>
En un ejemplo ms complejo, imagine que la asociacin entre Employee y Organization estuviere mantenida por una tabla Employment, llena de datos histricos de empleo. Entonces, una asociacin al empleador ms reciente de un empleado (el que tuviere la startDate ms reciente) podra ser mapeada de esta manera:
<join> <key column="employeeId"/> <subselect> select employeeId, orgId from Employments group by orgId having startDate = max(startDate) </subselect> <many-to-one name="mostRecentEmployer" class="Organization" column="orgId"/> </join>
Uno se puede poner bastante creativo con esta funcionalidad, pero normalmente es ms prctico manejar estos casos unando HQL o consultas Criteria.
96 de 198
http://www.hibernar.org/documentacion_es/castellano.html
return name; } public void setName(Name name) { this.name = name; } ...... ...... } public class Name { char initial; String first; String last; public String getFirst() { return first; } void setFirst(String first) { this.first = first; } public String getLast() { return last; } void setLast(String last) { this.last = last; } public char getInitial() { return initial; } void setInitial(char initial) { this.initial = initial; } }
Ahora, Name puede ser persistida como un componente de Person (en ingls, "nombre" y "persona", respectivamente). Note que Name define mtodos getter y setter para sus propiedades persistentes, pero no necesita declarar ninguna interfaz ni propiedades identiicadoras. Nuestro mapeo Hibernate se vera as:
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name"> <!-- el atributo 'class" es optativo --> <property name="initial"/> <property name="first"/> <property name="last"/> </component> </class>
La tabla PERSON tendra las columnas pid, birthday, initial, first y last. Como todos los "value types", los componentes no soportan referencias compartidas. Dos personas pueden tener el mismo nombre, pero los dos objetos Person correspondientes contendrn dos objetos nombre independientes, slo que con "el mismo" valor. La semntica de nulo de un componente es ad hoc. Cuando se recargue el objeto contenedor, Hibernate assumir que el componente entero es nulo. Esto debera alcanzar para la mayora de los casos. Las propiedades de un componente pueden ser de cualquier tipo Hibernate (colecciones, asociaciones de-muchos-a-uno, otros componentes, etc). Los componentes anidados no deben ser considerados una rareza. Se espera que Hibernate soporte un modelo de objetos muy jerrquico y detallado en este sentido. El elemento <component> acepta un subelemento <parent> que se mapee a una propiedad desde la clase del componente como referencia de vuelta a la entidad contenedora.
<class name="eg.Person" table="person"> <id name="Key" column="pid" type="string"> <generator class="uuid"/> </id> <property name="birthday" type="date"/> <component name="Name" class="eg.Name" unique="true"> <parent name="namedPerson"/> <!-- referencia de vuelta a Person --> <property name="initial"/>
97 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Nota: si se define un Set de elementos compuestos, es importante implementar equals() y hashCode() correctamente. Los elementos compuestos pueden contener otros componentes, pero no colecciones. Si su componente compuesto a su vez contiene componentes, use la tag <nested-composite-element>. ste es un caso batante extico: una coleccin de componentes que a su vez tengan componentes. A este punto usted debera preguntarse si no sera ms apropiada una asociacin de-uno-a-muchos. Trate de remodelar el elemento compuesto como una entidad, pero dse cuenta de que, aunque el modelo de Java es el mismo, le modelo relacional y la semntica de persistencia son an ligeramente diferentes. Por favor, note que un mapeo de elemento compuesto no soporta propiedades anulables si se est usando un <set>. Hibernate tiene que usar el valor de cada columna para identificar un registro cuando se borra objetos (no hay una clave primaria separada en la tabla de elementos compuestos), lo cual no es posible con valores nulos. Se deber o bien usar slo valores no nulos en un <composite-element>, o bien elegir una <list>, <map>, <bag> o <idbag>. Un caso especial de elemento compuesto ese el elemento compuesto que tiene un elemento <many-to-one> anidado. Un mapeo como ste permite mapear columnas extra de una tabla de asociacin de-muchos-a-muchos a la clase del elemento compuesto. La siguiente es una asociacin de-muchos-a-muchos de from Order a Item (de orden a tem) en donde la fecha de compra ( purchaseDate), el precio (price) y la cantidad (quantity) son propiedades de la asociacin.
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.Purchase"> <property name="purchaseDate"/> <property name="price"/> <property name="quantity"/> <many-to-one name="item" class="eg.Item"/> <!-- el atributo "clase" es optativo --> </composite-element> </set> </class>
Por supuesto, no puede haber una referencia a la compra del otro lado, para proveer navegacin bidireccional de la asociacin. Recuerde que los componentes son "value types", y no aceptan referencias compartidas. Una simple compra (Purchase) puede estar en el set de una Order, pero no puede ser referida por el Item al mismo tiempo. Incluso son posibles asociaciones ternarias o cuaternarias:
<class name="eg.Order" .... > .... <set name="purchasedItems" table="purchase_items" lazy="true"> <key column="order_id"> <composite-element class="eg.OrderLine"> <many-to-one name="purchaseDetails class="eg.Purchase"/> <many-to-one name="item" class="eg.Item"/> </composite-element> </set>
98 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</class>
Los elementos compuestos pueden aparecer en consultas, usando la misma sintaxis que las entidades.
<many-to-one name="order" class="Order" insert="false" update="false"> <column name="orderId"/> <column name="customerId"/> </many-to-one> .... </class>
Ahora, cualquier clave fornea que se refiera a la tabla ORDERLINE, tambin ser compuesta. Esto se debe declarar en los mapeos para otras clases. La asociacin con OrderLine sera mapeada as:
<many-to-one name="orderLine" class="OrderLine"> <!-- el atributo "class" es optativo, como de costumbre --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </many-to-one>
(Note que la tag <column> es una alternativa al atributo column en todos lados.) Una asociacin de-muchos-a-muchos a OrderLine tambin usa la clave fornea compuesta.
<set name="undeliveredOrderLines"> <key column name="warehouseId"/> <many-to-many class="OrderLine"> <column name="lineId"/> <column name="orderId"/>
99 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(El elemento <one-to-many>, como es usual, no declara ninguna columna). Si la OrderLine misma posee una coleccin, tambin tiene una clave fornea compuesta.
<class name="OrderLine"> .... .... <list name="deliveryAttempts"> <key> <!-- una coleccin hereda el tipo de clave compuesta --> <column name="lineId"/> <column name="orderId"/> <column name="customerId"/> </key> <list-index column="attemptId" base="1"/> <composite-element class="DeliveryAttempt"> ... </composite-element> </set> </class>
La semntica de un mapeo <dynamic-component> es idntica a la de <component>. La ventaja de este tipo de mapeo es la capacidad de determinar las verdaderas propiedades del bean en tiempo de despliegue (deployment), simplemente editando el archivo de mapeo. Tambin es posible la manipulacin en tiempo de ejecucin del documento de mapeo, usando un parser DOM. Mejor an, se puede acceder al meta-modelo de tiempo de configuracin de Hibernate (y cambiarlo) usando el objeto Configuration .
100 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Adicionalmente, Hibernate soporta una cuarta clase de polimorfismo, ligeramente diferente: polimorfismo implcito Es posible usar estrategias de mapeo diferentes para distintas ramas de la misma jerarqua de herencias, y despus hacer uso del polimorfismo implcito para lograr polimorfismo todo a lo largo de dicha jerarqua. Sin embargo, Hibernate no soporta mezclar mapeos de <subclass>, and <joined-subclass> y <union-subclass> bajo el mismo elemento <class> de la clase raz. S es posible mezclar las estrategias de "una tabla por jerarqua" y de "una tabla por subclase" bajo el mismo elemento <class>, combinando los elementos <subclass> y <join> (vase a continuacin). Es posible definir mapeos de subclass, union-subclass, y joined-subclass en documentos de mapeo separados, justo debajo de hibernate-mapping. Esto permite extender la jerarqua de clases, simplemente agregando un nuevo archivo de mapeo. Se debe especificar el atributo extends en el mapeo de la subclase, nombrando una superclase previamente mapeada. Nota: en el pasado, esta caracterstica haca que el orden de los documentos de mapeo fuese relevante. Desde Hibernate 3, el orden no importa cuando se usa la palabra "extends". El orden dentro de cada documento individual, an tiene que tener las superclases antes de las subclases.
<hibernate-mapping> <subclass name="DomesticCat" extends="Cat" discriminator-value="D"> <property name="name" type="string"/> </subclass> </hibernate-mapping>
Se requiere exactamente una tabla. Con esta estrategia de mapeo, hay una gran limitacin: las columnas declaradas por las subclases, como CCTYPE, no pueden tener constraints NOT NULL.
101 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </joined-subclass> <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> </class>
Se requieren 4 tablas. Las tres tablas de subclases tienen asociaciones por clave primaria a la tabla de la superclase (de modo que el modelo relacional es en realidad una asociacin de-uno-a-uno).
La declaracin optativa fetch="select" le dice a Hibernate que no capture los datos de la subclase ChequePayment usando un outer join cuando se consulte a la superclase.
9.1.4. Mezclar "una tabla por jerarqua de clases" con "una tabla por subclase"
Incluso se pueden mezclar las estrategias de "una tabla por jerarqua de clases" con "una tabla por subclase" usando este abordaje:
<class name="Payment" table="PAYMENT">
102 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
Para cualquiera de estas estrategias de mapeo, una asociacin polimrfica a la clase raz Payment se mapea usando <many-to-one>.
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
Para las subclases, hay tres clases involucradas. Cada tabla define columnas para todas las propiedades de la subclase, incluso las propiedades heredadas. La limitacin de este abordaje, es que si la propiedad est mapeada en la superclase, el nombre de la columna debe ser el mismo en todas las tablas de subclases. (Podemos relajar este requisito en versiones futuras de Hibernate). La estrategia de generador de identidad no est permitida para una herencia que est usando union-subclass. Lgico, dado que la clave primaria debe ser compartida por todas las subclases de la jerarqua participantes en la unin. Si su superclase es abstracta, mapela con abstract="true". Por supuesto, si no es abstracta, se necesita una tabla adicional (por defecto sera PAYMENT en el ejemplo anterior) para almacenar instancias de la superclase.
9.1.6. Una tabla por cada clase concreta, usando polimorfismo implcito
Un enfoque alternativo es usar polimorfismo implcito:
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/>
103 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</id> <property name="amount" column="CREDIT_AMOUNT"/> ... </class> <class name="CashPayment" table="CASH_PAYMENT"> <id name="id" type="long" column="CASH_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CASH_AMOUNT"/> ... </class> <class name="ChequePayment" table="CHEQUE_PAYMENT"> <id name="id" type="long" column="CHEQUE_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CHEQUE_AMOUNT"/> ... </class>
Ntese que no mencionamos explcitamente en ningn lado la interfaz Payment. Tambin ntese que las propiedades de Payment estn mapeadas en cada una de las subclases. Si se quiere evitar la duplicacin, considere usar entidades XML (por ejemplo, [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ] en la declaracin de DOCTYPE y &allproperties; en el mapeo). La desventaja de este enfoque, es que Hibernate no generar SQL UNIONs cuando ejecute consultas polimrficas. Segn esta estrategia de mapeo, una asociacin polimrfica a Payment sera normalmente mapeada usando <any>.
<any name="payment" meta-type="string" id-type="long"> <meta-value value="CREDIT" class="CreditCardPayment"/> <meta-value value="CASH" class="CashPayment"/> <meta-value value="CHEQUE" class="ChequePayment"/> <column name="PAYMENT_CLASS"/> <column name="PAYMENT_ID"/> </any>
104 de 198
http://www.hibernar.org/documentacion_es/castellano.html
De nuevo no necesitamos mencionar la interfaz Payment explcitamente. Hibernate devuelve automticamente instancias de, por ejemplo, CreditCardPayment y sus subclases, que tambin implementan Payment, pero no instancias de NonelectronicTransaction.
9.2. Limitaciones
Hay ciertas limitaciones del abordaje a la estrategia "una tabla por clase concreta" usando polimorfismo implcito. Y hay limitaciones un tanto menos restrictivas para los mapeos que usan <union-subclass>. Una comparacin de las limitaciones de ambas estrategias se muestra en la tabla siguiente: Tabla 9.1. Caractersticas de los mapeos de herencia polimrfica Estrategia de polimrfica polimrfica polimrfica de-muchosherencia de-muchos-a-uno de-uno-a-uno de-uno-a-muchos a-muchos una tabla por jerarqua de clase
load()/get()
polimrficos
<de-muchosa-uno>
<de-unoa-uno>
<de-unoa-muchos>
una tabla por subclase una tabla por clase concreta (unionsubclass)
<de-muchosa-uno>
<de-unoa-uno>
<de-unoa-muchos>
<de-muchosa-uno>
<de-unoa-uno>
only)
not supported
not supported
<manyto-any>
105 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Transitorio (en ingls, transient): un objeto es transitorio cuando ha sido instanciado usando el operador new , y no est asociado con una sesin de Hibernate. No tiene representacin persistente en la base de datos y no se le ha asignado ningn identificador. Las instancias transitorias sern destruidas por el recolector de basura (garbage collector), si la aplicacin ya no tiene ninguna referencia a ellas. Use la sesin de Hibernate para volver persistente un objeto (y deje que Hibernate se encargue de los comandos SQL que sean necesarios para dicha transicin). Persistente (en ingls, persistent): una instancia persistente ya tiene una representacin en la base de datos, y un valor de identificador. Aunque recin haya sido cargada o grabada, por definicin ya est inscripta en el alcance de la sesin. Hibernate detectar cualquier cambio que se efecte sobre un objeto en estado persistente, y sincronizar su estado con el de la base de datos cuando la unidad de trabajo se complete. Si el objeto persistente es tranformado en transitorio, el programador no necesita ejecutar comandos UPDATE ni DELETE manualmente. Desprendido (en ingls, detached): un objeto desprendido es un objeto que era persistente, pero cuya sesin ha sido cerrada. La referencia al objeto an es vlida, y la instancia desprendida puede incluso ser modificada en este estado. Una instancia desprendida puede ser reasociada a una nueva sesin en el futuro, volvindola persistente de nuevo (junto con todas las modificaciones que haya sufrido). Esta caracterstica es ideal para programar unidades de trabajo largas, que necesiten darle a la aplicacin tiempo para "pensar". Las llamamos transacciones "de la aplicacin", esto es, una unidad del trabajo desde el punto de vista del usuario. Ahora discutiremos los estados y las transiciones entre ellos (as como los mtodos de Hibernate que disparan estas transiciones) con ms detalle.
Si Cat tiene un identificador autogenerado, dicho identificador se genera y se le asigna a cat cuando save() sea llamado. Si Cat tiene un identificador asignado externamente, o una clave compuesta, dicho identificador debera serle asignado a cat antes de invocar save(). Se puede usar persist() em lugar de save(), con la semntica definida en el borrador temprano de EJB3.
persist() convierte una instancia transitoria en persistente. Pero no garantiza que el identificador le vaya a ser asignado a la instancia inmediatamente: la asignacin puede ocurrir al aplicrsele "flush" a la sesin. persist() tambin garantiza que nos ejecutar un comando INSERT si es llamado fuera de los lmites de una transaccin. Esto es til para conversaciones de largo aliento, con un contexto de sesin/persistencia extendido. save() s garantiza la devolucin de un identificador, Si hay que ejecutar un INSERT (por ejemplo, porque el generador es "identity" y no "sequence") el INSERT ocurre inmediatamente, sin importar si se est dentro o fuera de una transaccin. Esto es indeseable en conversaciones largas, con un contexto de sesin/persistencia extendido.
Alternativamente, se puede asignar el identificador usando una versin sustituida (overloaded) de of save().
DomesticCat pk = new DomesticCat(); pk.setColor(Color.TABBY); pk.setSex('F'); pk.setName("PK"); pk.setKittens( new HashSet() ); pk.addKitten(fritz); sess.save( pk, new Long(1234) );
Si el objeto que se hace persistente tiene objetos asociados, (por ejemplo, la coleccin "kittens", gatitos, en el ejemplo anterior), dichos objetos pueden ser hechos persistentes en cualquier orden, a menos que exista una constraint NOT NULL en una columna de clave fornea. El riesgo de violar una constraint de clave fornea no existe, pero s se puede llegar a violar una constraint NOT NULL si se les aplica save() a los objetos en el orden incorrecto. Normalmente, usted no debe preocuparse por estos detalles, dado que, muy probablemente, usted terminar usando la caracterstica de Hibernate llamada persistencia transitiva para grabar los objetos asociados automticamente. Con ella, ni siquiera las violaciones de NOT NULL ocurren, Hibernate se hace cargo de todo. La persistencia transitiva se discute ms
106 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Dese cuenta de que load() lanzar una excepcin irrecuperable si no existe la fila de base de datos correspondiente. Si la clase es mapeada con un proxy, load() simplemente devuelve un proxy no inicializado, y no hay contacto con la base de datos hasta que realmente se invoque un mtodo del proxy. Este comportamiento es muy til si se desea crear una asociacin con un objeto, sin realmente cargarlo desde la base de datos. Tambin permite cargar mltiples instancias en lotes, si batch-size est definido para la clase. Cuando no se est seguro de que exista una fila correspondiente, debera usarse el mtodo get(), el cual consulta la base de datos inmediatamente y devuelve null si no existe una fila correspondiente.
Cat cat = (Cat) sess.get(Cat.class, id); if (cat==null) { cat = new Cat(); sess.save(cat, id); } return cat;
Incluso se puede cargar un objeto usando un SQL SELECT ... FOR UPDATE, usando LockMode. Vea la documentacin de la API para ms informacin.
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
Note que ni las instancias asociadas ni las colecciones contenidas son seleccionadas FOR UPDATE (con el propsito de ser modificadas), a menos que se especifiquen los valores lock o all en el parmetro de mapeo "cascade". Se puede recargar un objeto y todas sus asociaciones an cualquier momento, usndo el mtodo refresh(). Esto es muy til cuando se estn usando triggers de DB para inicializar algunas de las propiedades del objeto.
sess.save(cat); sess.flush(); //fuerza el SQL INSERT sess.refresh(cat); //relee el estado (luego de que el trigger se ejecuta)
Llegados a este punto,usualmente se plantea una cuestin: Cunto carga Hibernate de la base de datos?Cuntos SQL SELECTs se usan? Esto depende de la estrategia de captura (fetching strategy) y se explica en la Seccin 19.1, Estrategias de captura (fetch).
10.4. Consultas
Si no se conocen los identificadores de los objetos que se est buscando, se necesita una consulta (query). Hibernate soporta un lenguaje para consultas poderoso pero fcil de usar: HQL (las siglas en ingls de "Hibernate Query Language"). Para la creacin programtica de consultas, Hibernate soporta dos clases muy sofisticadas, Criteria y Example (a veces referidos como QBC y QBE, por "query by criteria" y "query by example", respectivamente). Tambin se puede expresar la consulta en el lenguaje nativo de su base de datos, con soporte opcional de conversin de su resultado en objetos.
107 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Una consulta normalmente se ejecuta invocando list(), el resultado de la consulta ser cargado completamente en una coleccin en memoria. Las entidades de instancia que son devueltas estn en estado persistente. El mtodo uniqueResult() ofrece un atajo si usted ya sabe que la consulta devolver un solo objeto. Note que las consultas que hacen uso de "captura ansiosa" (eager fetching), normalmente devuelven duplicados del objeto raz, con sus colecciones inicializadas. Estos duplicados se pueden eliminar, simplemente usando un Set.
108 de 198
http://www.hibernar.org/documentacion_es/castellano.html
10.4.1.5. Paginacin
Si se necesita establecer lmtes en el resultset (el mximo nmero de filas que se quiere capturar / desde qu fila se desea obtener datos), deberan usarse los siguientes mtodos de la interfaz Query:
Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();
Hibernate sabe cmo traducir este lmite al el SQL nativo de su base de datos.
109 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Ntese que para esta funcionalidad se requiere una conexin abierta a la base de datos. y un cursor. Use setMaxResult()/setFirstResult() si necesita funcionalidad de paginacin desconectada.
Note que el cdigo del programa propiamente dicho es independiente del lenguaje de consultas que se use; se pueden definir consultas SQL nativas en metadata, o migrar consultas existentes a Hibernate colocndolas en archivos de mapeo. Tambin note que la declaracin dentro de un elemento <hibernate-mapping> requiere un nombre globalmente nico para la consulta, mientras que la declaracin de una consulta dentro de un elemento <class> se vuelve nica automticamente al afijarle el nombre enteramente calificado de la clase, por ejemplo, eg.Cat.PorNombreYPesoMaximo.
110 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La coleccin devuelta se considera una bag, y es una copia de a coleccin dada. La coleccin original no es modificada (lo cual es conceptualmente opuesto a la idea de un "filtro", pero consistente en cuanto al resultado que se espera). Observe que los filtros no requieren una clusula FROM (aunque pueden tenerla si es necesario). Los filtros no estn limitados a devolver los elementos de colecciones mismos.
Collection blackKittenMates = session.createFilter( pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK.intValue") .list();
Incluso un filtro con su consulta vaca es til, por ejemplo para cargar un subconjunto de elementos en una coleccin enorme:
Collection tenKittens = session.createFilter( mother.getKittens(), "") .setFirstResult(0).setMaxResults(10) .list();
Las APIs de Criteria y su pariente Example se discuten con ms detalle en Captulo 15, Consultas Criteria.
Las consultas SQL pueden contener parmetros nombrados o posicionales, al igual que las consultas de Hibernate. Se puede encontrar ms informacin sobre las consultas SQL nativas en Captulo 16, SQL Nativo.
111 de 198
http://www.hibernar.org/documentacion_es/castellano.html
A veces, este modelo de programacin es ineficiente, dado que requerira tanto un SQL SELECT (para cargar el objeto) como un SQL UPDATE (para persistir su estado modificado) en la misma sesin. Por lo tanto, Hibernate ofrece un abordaje alternativo, usando instancias desprendidas: Note que Hibernate no expone una API propia para ejecutar comandos UPDATE o DELETE directamente. Hibernate es un servicio de manejo de persistencia, y no se lo debe concebir en trminos de qu comandos ejecuta. JDBC es una API perfectamente adecuada para ejecutar comandos SQL, se puede obtener una conexin de JDBC en cualquier momento invocando session.connection(). Ms an, la nocin de "operaciones en masa" entra en conflicto con el mapeo objeto/relacional para las aplicaciones orientadas al procesamiento de transacciones en lnea. Puede ser, sin embargo, que versiones futuras de Hibernate provean funciones especiales para procesamiento en masa. Vase Captulo 13, Procesamiento en lotes para posibles trucos de operacin en lote.
Si el Cat con identificador catId ya hubiera sido cargado por la segunda sesin (secondSession) cuando la sesin trataba de reasociarlo, se habra producido una excepcin. Use update() si usted est seguro de que la sesin no contiene una instancia que ya es persistente con el mismo id, merge() si usted desea consolidar sus modificaciones en cualquier momento, independientemente del estado de esa instancia en la sesin. En otras palabras, update() es normalmente el primer mtodo que se invocar en una sesin nueva, garantizando que la reasociacin de las instancias desprendidas sea la primera operacin que ocurra. La aplicacin debera aplicarles update() a las instancias desprendidas dependientes de la instancia desprendida en cuestin, si y slo si desea que su estado tambin sea actualizado. Esto puede ser automatizado, por supuesto, usando persistencia transitiva, vea la Seccin 10.11, Persistencia transitiva. El mtodo lock() tambin le permite a la aplicacin reasociar un objeto con una sesin nueva. Sin embargo, la instancia desprendida no debe haber sido modificada!
//simplemente reasocia sess.lock(fritz, LockMode.NONE); //primero verifique la versin, luego reasocie sess.lock(izi, LockMode.READ); //primero verifique la versin usando SELECT ... FOR UPDATE, luego reasocie sess.lock(pk, LockMode.UPGRADE);
Note que lock() puede ser usado con varios LockModes, vea la documentacn de la API y el captulo sobre manejo de transacciones para ms informacn. Reasociar no es el nico uso que lock() tiene. Otros modelos para unidades largas de trabajo se discuten en Seccin 11.3, Control de concurrencia optimista.
112 de 198
http://www.hibernar.org/documentacion_es/castellano.html
un identificador nuevo), o bien actualice/reasocie las instancias desprendidas asociadas con el identificador actual. El mtodo saveOrUpdate() implementa tal funcionalidad:
// en la primera sesin Cat cat = (Cat) firstSession.load(Cat.class, catID); // en una capa ms alta de la aplicacin Cat mate = new Cat(); cat.setMate(mate); // luego, en una nueva sesin secondSession.saveOrUpdate(cat); secondSession.saveOrUpdate(mate);
// actualice el estado existente (cat tiene un id no nulo) // grabe la instancia nueva (mate tiene un id nulo)
El uso y semntica de saveOrUpdate() les parece confuso a los nuevos usuarios. En primer lugar, a menos que se est intentando usar instancias de una sesin en otra sesin nueva, no hay necesidad de usar update(), saveOrUpdate(), ni merge(). Hay aplicaciones enteras que jams utilizarn ninguno de estos mtodos. Usualmente, update() o saveOrUpdate() se usan en el siguiente escenario: la aplicacin carga un objeto en la primera sesin el objeto es pasado a la capa de interfaz de usuario se le hacen algunas modificaciones al objeto el objeto baja de nuevo a la capa de lgica de negocios la aplicacin persiste estas modificaciones invocando update() en una segunda sesin
saveOrUpdate() hace
lo siguiente
si el objeto ya es persistente en esta sesn, no hace nada si ya hay otro objeto asociado con la sesin con el mismo identificador, se produce una excepcin si el objeto no tiene una valor de identificador, se invoca save() si el identificador del objeto es asignable, y le ha sido recientemente asignado a una instancia recin creadam se invoca save(). si el objeto es versionado (por una <version> o <timestamp>), y la propiedad versin es el mismo valor asignado a un objeto recin instanciado, se invoca save() en cualquier otro caso, se invoca update() y merge() es muy distinto si ya hay una instancia persistente con el mismo identificador asociada con la sesin, copia el estado del objeto dado en la instancia persistente. si no hay ninguna instancia asociada con la sesin, intenta cargarla de la base de datos, o crear una nueva instancia persistente. se devuelve la instancia persistente la instancia dada (como parmetro) no se vuelve asociada con la sesin, permanece desprendida.
Se puede borrar objetos en cualquier orden que se desee, sin temor de causar violaciones de clave fornea. Pero s es
113 de 198
http://www.hibernar.org/documentacion_es/castellano.html
posible violar constraints NOT NULL aplicadas a la columna de clave fornea, por ejemplo si se borra al padre antes que al hijo.
El ReplicationMode determina cmo el mtodo replicate() lidiar con conflictos que puedan existir con filas existentes en la base de datos.
ReplicationMode.IGNORE: ignorar
el objeto cuando exista una fila de base de datos con el mismo identificador. fila existente en la base de datos que tenga el mismo
identificador.
ReplicationMode.EXCEPTION: lanzar una
excepcin si ya existe una fila en la base de datos que tenga el mismo fila si su nmero de versin es ms antiguo que el del objeto.
identificador.
ReplicationMode.LATEST_VERSION: sobrescribir la
En caso contrario, ignorar. Algunos casos de uso para esta caracterstica incluyen: reconciliar datos ingresados en dos instancias distintas de base de datos, actualizar la informacin de configuracin del sistema durante la actualizacin de un producto, dar marcha atrs (rollback) con cambios hechos durante transacciones carentes de integridad transaccional (non-ACID), y muchos ms.
114 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Session.delete()
(una excepcin es que los objetos que usen la generacin de identificador native sern insertados al invocar save). A excepcin de cuando se invoque flush() explcitamente, no hay absolutamente ninguna garanta acerca de cundo los llamados JDBC sern ejecutados por la sesin, slo acerca del orden en que sern ejecutados. Sin embargo, Hibernate garantiza que Query.list(..) nunca devolver datos rancios. Es posible cambiar el comportamiento por defecto para que el "flush" ocurra menos frecuentemente. La clase FlushMode define tres modos diferentes: slo provocar el "flush" al momento de llamar "commit" (y esto slo en una transaccin de la API de Hibernate), provocar el "flush" automticamente usando la rutina explicada, o nunca provocar el "flush" menos que flush() sea invocado explcitamente. Este ltimo modo es til para unidades de trabajo largas, en donde la sesin se mantiene abierta y desconectada por largo tiempo (vase la Seccin 11.3.2, Sesin extendida y versionado automtico).
sess = sf.openSession(); Transaction tx = sess.beginTransaction(); sess.setFlushMode(FlushMode.COMMIT); // permite que las consultas devuelvan datos rancios Cat izi = (Cat) sess.load(Cat.class, id); izi.setName(iznizi); // podra devolver datos rancios sess.find("from Cat as cat left outer join cat.kittens kitten"); // el cambio a izi no ha tenido "flushing"! ... tx.commit(); // flush occurs sess.close();
Durante el "flush", puede ocurrir una excepcin (por ejemplo, si una operacin de creacin de datos viola una constraint). Como el manejar excepciones involucra algn conocimiento del comportamiento transaccional de Hibernate, lo discutiremos en el Captulo 11, Transacciones y concurrencia.
Incluso se puede usar cascade="all" para indicar que todas las operaciones deben ser propagadas en direccin de esa asociacin. El valor por defecto, cascade="none", especifica que ninguna operacin debe ser propagada.
115 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Un tipo especial de propagacin en cascada, delete-orphan, se aplica slo a las asociaciones de-uno-a-muchos, e indica que la operacin delete() debe ser aplicada a cualquier hijo cuyo padre haya sido borrado. Recomendaciones:
to-many>.
Normalmente no tiene sentido habilitar la propagacin en cascada para asociaciones <many-to-one> o <manyLa propagacin es usualmente til para asociaciones <one-to-one> y <one-to-many>.
Si el ciclo del vida del hijo est ligado al del padre, convirtalo en un objeto de ciclo de vida especificando cascade="all,delete-orphan". En cualquier otro caso, puede que usted no necesite usar cascade en absoluto. Pero si usted calcula que deber trabajar a menudo con el padre y los hijos en la misma transaccin, y quiere ahorrarse algo de tecleo, considere usar cascade="persist,merge,save-update". Mapear una asociacin (ya sea de un solo valor, o de una coleccin) con cascade="all" marca la asociacin como "del estilo padre/hijo", en donde una grabacin/actualizacin/borrado del padre resulta en una grabacin/actualizacin/borrado de los hijos. Ms an, una mera referencia a un hijo desde un padre persistente resultar en la grabacin/actualizacin del hijo. Lo inverso no se da, sin embargo. Un hijo a quien el padre deje de hacer referencia no es automticamente borrado, excepto en el caso de una asociacin de-uno-a-muchos mapeada con cascade="delete-orphan". La semntica exacta de la relacin padre/hijo es como sigue: Si se le pasa un padre (como parmetro) a un persist(), todos sus hijos sern tambin pasados a persist() Si se le pasa un padre a un merge(), todos sus hijos sern tambin pasados a merge() Si se le pasa un padre a un save(), update() o saveOrUpdate(), todos sus hijos sern tambin pasados a saveOrUpdate(). Si un hijo transitorio o desprendido empieza a ser referido por un padre persistente, se le pasa a saveOrUpdate() si un padre es borrado, todos sus hijos le son pasados a delete() Si un hijo deja de ser referido por un padre persistente, no pasa nada en especial: la aplicacin debera borrar el hijo explcitamente si hace falta (a menos que haya cascade="delete-orphan", en cuyo caso la clase el hijo que se ha vuelto hurfano ser borrado). Por ltimo, note que la propagacin de operaciones en cascada se le puede aplicar a un objeto al momento de invocarlo o al momento de efectuar "flush". Todas las operaciones, si estn habilitadas, se propagan en cascada hacia todas las entidades que estn a su alcance cuando la operacin es ejecutada. Sin embargo, save-upate y delete-orphan son transitivas hacia las entidades asociadas cuando ocurre el "flush" de la sesin.
116 de 198
http://www.hibernar.org/documentacion_es/castellano.html
117 de 198
http://www.hibernar.org/documentacion_es/castellano.html
capa de persistencia Hibernate), se abre una nueva sesin, y todas las operaciones de base de datos son ejecutadas e esta unidad de trabajo. Una vez que el trabajo se haya completado (y la respuesta para el cliente se haya preparado), la sesin sufre un "flush" y se cierra. Tambin se usara una sola transaccin de base de datos para atender la solicitud del cliente, comenzndola y efectuando "commit" cuando se abra y cierre la conexin, respectivamente. La relacin entre sesin y transaccin es una a una, y este modelo es perfectamente adecuado para muchas aplicaciones. El desafo radica en la implementacin. Hibernate ya trae un sistema incluido que se puede usar para simplificar el uso de este patrn, y manejar lo que se considera la "sesin actual". Todo lo que el programador debe hacer, es comenzar una transaccin cuando haya que procesar una solicitud (request) al servidor, y finalizar la transaccin antes de que la respuesta le sea enviada al cliente. Esto se puede lograr de varias maneras: las soluciones ms comunes son: -un filtro (ServletFilter) -un interceptor basado en AOP, que tenga sus "pointcuts" en los mtodos del servicio, -un contenedor de proxies/intercepciones, etc. Una manera estndar de implementar aspectos que conciernen de una misma forma a varios niveles y reas de la aplicacin (en ingls, "cross-cutting aspects"), es usar un contenedor de EJB, el cual puede implementar transacciones de una manera declarativa y manejada por el contenedor mismo (CMT). Si se decide usar la demarcacin de transacciones programtica, prefirase la API de Transaction de Hibernate, que es fcil y porttil. El cdigo de su aplicacin puede acceder a la "sesin actual" para procesar una request, simplemente invocando en cualquier lugar, y tan a menudo como haga falta. Siempre se obtendr una sesin que estar dentro del alcance o "scope" de la transaccin de base de datos actual. Esto tiene que ser configurado, ya sea para entornos de recursos locales o para JTA (vase la Seccin 2.5, Sesiones contextuales.
sessionFactory.getCurrentSession()
A veces es conveniente extender los alcances de la sesin y de la transaccin de base de datos hasta que la vista o "view" le haya sido presentada al usuario. Esto es especialmente til en aplicaciones basadas en servlets, las cuales utilizan una fase separada de presentacn posterior al procesamiento de la solicitud o "request". Extender la transaccin de base de datos hasta que la "view" sea presentada es fcil si se implementa un interceptor propio. Sin embargo, no es fcil de hacer si uno se apoya en un EJBs con transacciones CMT, dado que la transaccin se completar tras el return de los mtodos de los EJBs, antes de que la presentacin de ninguna view hubiere comenzado. Visite el sitio de web de Hibernate para consejos y ejemplos acerca de este patrn Open Session in View.
118 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Objetos desprendidos: si se decide usar el patrn una sesin por solicitud (session-per-request) ya discutido, todas las instancias cargadas se convertirn en "desprendidas" (detached) durante el tiempo que el usario se tome para pensar. Hibernate le permite reasociar estos objetos y persistir las modificaciones; este patrn se llama una-sesinpor-solicitud-con-objetos-desprendidos. Para aislar modificaciones concurrentes se usa versionado automtico. Sesin larga (o "extendida"): la sesin de Hibernate puede desconectarse de la conexin JDBC subyacente luego de que la transaccin haya ejecutado su commit, y reconectada cuando ocurra una nueva solicitud del cliente. Este patrn se conoce como una-sesin-por-conversacin y hace que la reasociacin de objetos sea innecesaria. Para aislar modificaciones concurrentes se usa versionado automtico, y a la sesin no se le permite hacer "flush" automtico, sino explcito. Ambos patrones, una-sesin-por-spolicitud y una-sesin-por-conversacin tienen ventajas y desventajas. Las discutiremos ms adelante, en el contexto del control de concurrencia optimista.
Identidad de JVM
foo==bar
Entonces, para objetos asociados a una sesin en particular, (esto es, en el mismo alcance o "scope" de una sesin) las dos nociones son equivalentes, e Hibernate garantiza que la identidad JVM equivale a la identidad de BD. Sin embargo, si la aplicacin accede en forma concurrente al mismo dato, puede ocurrir que la misma identidad persistente est contenida en dos instancias de objeto en dos sesiones distintas. En este caso la identidad persistente o de base de datos existe, pero la identidad de JVM no, los objetos son "diferentes". Estos conflictos se resuelven a nivel usando versionado automtico (cuando ocurren los "flush"/"commit"), usando el enfoque optmista. Este enfoque deja que Hibernate se preocupe por la concurrencia. Tambin provee la mejor "escalabilidad", dado que garantizar la identidad slo a nivel de unidades de trabajo en un thread simple no requiere un costoso "lock" ni otros medios de sincronizacin. La aplicacin no necesita sincronizar ningn objeto, siempre y cuando se atenga a que se usar un solo thread por sesin. Dentro de una sesin, la aplicacin puede usar == tranquilamente para comparar objetos. Sin embargo, una aplicacin que use == fuera de una sesin, se puede topar con resultados inesperados. Esto puede ocurrir incluso en lugares inslitos, por ejemplo, si se colocan dos instancias desprendidas en el mismo Set, existe la posibilidad de que ambas tengan la misma identidad de base de datos (es decir, que representen la misma fila) pero no la misma identidad JVM. El programador debe sustituir los mtodos equals() y hashCode() en las clases persistentes, e implementar su propia nocin de igualdad entre objetos. Slo una advertencia: nunca use el identificador de base de datos para implementar igualdad; use una "clave de negocios", una combinacin nica y normalmente inmutable de atributos. Si la instancia transitoria es almacenada en un Set, cambiar el hashcode rompe el contrato del Set. Los atributos de las "claves de negocio" no necesitan ser tan estables como las claves primarias de una base de datos. Slo necesitan poder establecer, de manera estable, diferencias o igualdad entre los objetos que estn en un Set. Vea el sitio de web de Hibernate para una discusin ms detallada sobre este tema. Tambin note que ste no es un problema de Hibernate, sino la manera en que los objetos de Java implementan identidad e igualdad.
119 de 198
http://www.hibernar.org/documentacion_es/castellano.html
(HttpSession, la cual se discute ms adelante), se debera considerar el sincronizar el acceso a la sesin HTTP. De otra manera, cualquier usuario que cliquee "reload" lo suficientemente rpido, es probable que termine usando la misma sesin (de Hibernate) en dos threads ejecutndose en forma concurrente. Si Hibernate produce una excepcin, significa que hay que desandar (rollback) la transaccin de base de datos, y cerrar la sesin inmediatamente (como se discute luego en ms detalle),. Si la sesin est asociada a la aplicacin, debe detenerse la aplicacin. Efectuar un "rollback" de la transaccin no restaura los objetos de negocio al estado en el que estaban antes de que la transaccin ocurriese. Esto significa que el estado de la base de datos y el de los objetos de negocio s quedan fuera de fase. Normalmente esto no es problema, porque las excepciones no son recuperables, y de todos modos es necesario comenzar todo de nuevo luego de un "rollback". La sesin almacena en su cach cada objeto que est en estado persistente (habiendo vigilado y comprobado Hibernate si su estado es "sucio"). Esto significa que crecer para siempre, hasta provocar un error de memoria (OutOfMemoryException) si se la deja abierta por mucho tiempo o simplemente se le cargan demasiados datos. Una solucin para esto, es invocar clear() y evict() para manejar el cach de la sesin, pero lo que ms probablemente se debera considerar es un procedimiento de base de datos almacenado (stored procedure) si se necesitan operaciones masivas de datos. Algunas soluciones se muestran en el Captulo 13, Procesamiento en lotes. Mantener una sesin abierta durante toda la interaccin con el usuario tambin aumenta la probabilidad de que los datos se vuelvan "rancios".
120 de 198
http://www.hibernar.org/documentacion_es/castellano.html
No hace falta invocar el "flush" de la sesin explcitamente: el llamaod a commit() dispara automticamente la sincronizacin (dependiendo del Modo de "flush" para la sesin. Un llamado a close() marca el fi de la sesin. La implicancia ms importante de close() es que la conexin JDBC ser cedidad por la sesin. Este cdigo Java es porttil, y puede ejecutarse tanto en entornos administrados como no administrados. Una solucin muchio ms flexible (que ya viene incorporad en Hibernate), es el manejo del contexto de "sesin actual", como se describin anterioromente.
// Estilo para un entorno no-administrado con getCurrentSession() try { factory.getCurrentSession().beginTransaction(); // efectuar algo de trabajo ... factory.getCurrentSession().getTransaction().commit(); } catch (RuntimeException e) { factory.getCurrentSession().getTransaction().rollback(); throw e; // o mostrar un mensaje de error }
Se podrn encontrar fragmentos de cdigo como stos en cualquier aplicacin normal. Las excepciones fatales, de sistema, deberan ser capturadas en la capa superior. En otras plabras, el cdigo que ejecuta los llamados a Hibernate, en la capa de persistencia, y el cdigo que maneja las RuntimeExceptions (y normalmente hace las tareas de limpeza y salida) estn en capas diferentes. El "manejo de contexto actual" hecho por Hibernate puede simplificar este diseo considerablemente, todo lo que se necesita es accedeso a la SessionFactory. El manejo de excepciones se discute ms adelante en este captulo. Note que debera seleccionarse org.hibernate.transaction.JDBCTransactionFactory (el valor pord efecto), y, para el segundo ejemplo, el valor "thread" para hibernate.current_session_context_class.
121 de 198
http://www.hibernar.org/documentacion_es/castellano.html
tx = sess.beginTransaction(); // efectuar algo de trabajo ... tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); throw e; // o mostrar un mensaje de error } finally { sess.close(); }
Si se desea usar la sesin asociada a una transaccin, es decir, la funcionalidad getCurrentSession() para una ms fcil propagacin del contexto, se debe usar la API de JTA UserTransaction directamente:
// estilo BMT con getCurrentSession() try { UserTransaction tx = (UserTransaction)new InitialContext() .lookup("java:comp/UserTransaction"); tx.begin(); // efectuar algo de trabajo con la sesin asociada a la transaccin factory.getCurrentSession().load(...); factory.getCurrentSession().persist(...); tx.commit(); } catch (RuntimeException e) { tx.rollback(); throw e; // o mostrar un mensaje de error }
Con CMT, la demarcacin de transacciones se hace en los descriptores de despliegue (dployment descriptors) del session bean, no se hace programticamente. As que el cdigo queda reducido a:
// estilo CMT Session sess = factory.getCurrentSession(); // efectuar algo de trabajo ...
En un entorno CMT/EJB incluso el rollback ocurre automticamente, puesto que una RuntimeException no capturada emitida por el mtodo de un session bean le dice al contenedor que efecte rollback en la transaccin global. Esto significa que no hace falta usar la API de Transaction API de Hibernate en absoluto con BMT or CMT, e igualmente se obtiene propagacin automtica de la sesin "actual" asociada a la transaccin. Cuando se elige la fbrica (factory) de transacciones, note que debera elegirse org.hibernate.transaction.JTATransactionFactory si se usa JTA directamente (BMT), y debera usarse org.hibernate.transaction.CMTTransactionFactory en un session bean CMT. Ms an, asegrese de que su hibernate.current_session_context_class ha sido o bien eliminado (por compatibilidad hacia a trs o "backwards compatibility"), o bien puesto a "jta". La operacin getCurrentSession() tiene una desventaja en un entorno JTA:, Hay una advertencia sobre el uso del modo de liberacin de conexiones after_statement, el cual es el valor por defecto. Debido a una tonta limitacin de la especificacin JTA, para Hibernate no es posible limpiar automticamente instancias no cerrada de ScrollableResults o Iterator que hayan sido devueltas por scroll() o iterate(). Usted debe liberar el cursor de base de datos subyacente invocando explcitamente cursor by calling ScrollableResults.close() o Hibernate.close(Iterator) en el bloque finally. (Por supuesto, la mayora de las aplicaciones pueden fcilmente evitar usar en absoluto scroll() o iterate() en el cdigo JTA o CMT).
122 de 198
http://www.hibernar.org/documentacion_es/castellano.html
como recuperable. Asegrese de que la sesin ser cerrada invocando close() en el bloque finally. la cual envuelve la mayora de los errores que ocurren en la capa de persistencia de Hibernate, es una excepcin del tipo "unchecked" (esto es, que no requiere captura obligatoriamente en tiempo de compilacin). No lo era en versiones anteriores de Hibernate. En nuestra opinin, no se debera forzar al programador a capturar excepciones de bajo nivel en la capa de persistencia. En la mayora de los sistemas, las excepciones "unchecked" son manejadas en uno de los primeros "marcos" de la pila de invocaciones a mtodos (es decir, en las capas ms "altas" de la aplicacin), y se le presenta un mensaje de error al usuario de la aplicacin, o se adopta algn otro curso de accin apropiado. Note que Hibernate puede tambin emitir otras exceptiones, adems de HibernateException. stas son, de nuevo, no recuperables, y se debe adoptar las medidas apropiadas para lidiar con ellas. Hibernate envuelve las excepciones SQLException generadas al interactuar c con la base de datos en una JDBCException. De hecho, Hibernate intentar convertir la excepcin en una subclase de JDBCException que tenga ms sentido. La SQLException subyacente est siempre disponible via JDBCException.getCause(). Hibernate convierte las SQLException en subclases apropiadas de JDBCException usando el conversor SQLExceptionConverter que est asociado a la fbrica SessionFactory. POr defecto, el SQLExceptionConverter es definido de acuerdo al dialecto SQL elegido. Pero es posible enchufar una implementacin a medida (ver los javadocs para la clase SQLExceptionConverterFactory por detalles). Los subtipos estndar de JDBCException son:
JDBCConnectionException: indica SQLGrammarException: indica HibernateException,
un error sintctico o gramatical con el SQL emitido alguna forma de violacin de una constraint de integridad
solicitada.
GenericJDBCException: una
Session sess = factory.openSession(); try { //set transaction timeout to 3 seconds sess.getTransaction().setTimeout(3); sess.getTransaction().begin(); // do some work ... sess.getTransaction().commit() } catch (RuntimeException e) { sess.getTransaction().rollback(); throw e; // or display error message } finally { sess.close(); }
Note que setTimeout() no puede ser llamada desde un bean en CMT, en donde los "timeouts" de las transacciones son definidos declarativamente.
123 de 198
http://www.hibernar.org/documentacion_es/castellano.html
El nico abordaje consistente con una alta concurrencia y un alta "escalabilidad", es el control de concurrencia optimista con versionado. El chequeo de versiones usa nmeros de versin, o timestamps, para detectar adtualizaciones conflictivas (y evitar que se pierdan modificaciones). Hibernate ofrece 3 enfoques distintos para escribir una aplicacin que use concurrencia optimista. Los casos de uso que mostraremos ocurren en el contexto de una conversacin larga, pero el chequeo de versiones tiene la ventaja de tambin prevenir la prdida de modificaciones en transacciones de base de datos simples.
(N.del.T):"foo" y "bar", de origen incierto, son locuciones que se usan en ingls como ejemplos de nombres para cualquier cosa, especialmente en el mbito de la computacin. Si se usa "foo" como nombre en un ejemplo, casi siempre se espera que el siguiente nombre sea "bar". La propiedad version se mapea usando <version>, e Hibernate incrementar el nmero automticamente durante el "flush" si la entidad est sucia. Por supuesto, si se opera en un entorno de baja concurrencia de datos, y no se requiere un chequeo de versiones, se puede usar este abordaje pero saltearse el chequeo de versiones. En tal caso, la estrategia por defecto para conversaciones largas ser "el ltimo commit es el que gana". Tenga en cuenta que esto puede confundir a los usuarios de la aplicacin, dado que pueden experimentar prdidas de actualizaciones sin mensajes de error, o sufrir la posibilidad de conflictos cuando los cambios se sincronicen. Claramente, el chequeo manual de versiones slo es factible en circunstancias muy triviales, y en la mayora de las aplicaciones no es prctico. A menudo hay que chequear no slo instancias simples, sino todo un rbol de objetos modificados. Hibernate ofrece chequeo automtico de versin, usando uno de estos paradigmas de diseo: "sesin extendida", o "instancais desprendidas".
124 de 198
http://www.hibernar.org/documentacion_es/castellano.html
El objeto foo todava sabe en qu sesin fue cargado. Comenzar una nueva transaccin de base de datos en una sesin vieja, obtiene una nueva conexin y reanuda la sesin. Efectuar un "commit" de una transaccin de base de datos desconecta una sesin de la coinexin JDBC y devuelve la conexin al "pool". Tras la reconexin, para forzar un chequeo de versin en los datos que no se estn actualizando, se puede invocar Session.lock() con LockMode.READ en cualquier objeto que pueda haber sido actualizado por otra transaccin. Usted no necesita un "lock" sobre cualquier dato que usted est actualizando. Usualmente, se le debe asignar FlushMode.MANUAL a una sesin extendida, de manera que, en realidad, slo a la ltima transaccin de base de datos se le permita persistir todas las modificaciones hechas durante esa conversacin. Por eso, slo esta ltima transaccin de BD incluira la operacin flush(), y luego tambin el llamado de la sesin a close() para cerrar la conversacin. Este patrn es problemtico si la sesin es demasiado grande para como para ser almacenada durante el tiempo que el usuario se toma para reaccionar. Por ejemplo, una HttpSession debera mantenerse tan chica como sea posible. Como la sesin es tambin el cach de primer nivel, y contiene todos los objetos cargados, probablementa slo podamos usar esta estrategia en uns pocos ciclos solicitur/respuesta. Una sesin debera usarse para una sola conversacin, dado que pronto contendr datos "pasados". (Note que versiones anteriores de Hibernate requeran que la sesin se conectara y desconectara explcitamente. Dichos mtodos ahora son obsoletos (deprecated), y comenzar y terminar una transaccin tiene el mismo efecto). Tambin note que se debera mantener la sesin desconectada cerca de la capa de persistencia. En otras palabras, use un "stateful session bean" de EJB para contener la sesin en un entorno de tres capas, y no la transfiera al entorno de web (ni siquiera la serialice para transferirla a otra capa separada) para almacenarla en una HttpSession. El patrn de "sesin extendida" una-sesin-por-conversacin, es ms difcil de implementar con control automtico del contexto de sesin actual. Para esto se necesita proveer una implementacin a medida de CurrentSessionContext. Vea la Wiki de Hibernate para ejemplos:
De nuevo, Hibernate chequear ls versiones de instancia al ocurrir el "flush", lanzando una excepcin si han ocurrido actualizaciones conflictivas. Se puede tambin invocar lock() en lugar de update(), y usar LockMode.READ (efectuando un cheque de versin, eludiando todos los cachs) si se est seguro de que el objeto no ha sido modificado.
125 de 198
http://www.hibernar.org/documentacion_es/castellano.html
cuando ocurra el "flush". En ambos casos, sea con columnas dedicadas de versin/timestamp, o con comparacin completa/de campos sucios, Hibernate usa un solo comando UPDATE (con una clusula WHERE apropiada) por entidad para ejecutar el chequeo de versin y actualizar la informacin. Si se desa usar persistencia transitiva para la reasociacin en cascada de entidades relacionadas, es posible que Hibernate ejecute algunos UPDATEs innecesarios. Esto normalmente no es un problema, pero puede que se ejecuten triggers on update en la base de datos, cuando no se les ha efectuado ningn cambio a las instancias desprendidas. Se puede personalizar este comportamiento ms a medida, asignando select-before-update="true" em el mapeo de <class>, forzando a Hibernate a practicar un SELECT de la instancia para asegurarse de que realmente hayan ocurrido cambios, antes de actualizar la fila.
puede ser adquirido ante una solicitud explcita del usuario, usando SELECT ... FOR UPDATE en una base de datos que soporte dicha sintaxis.
LockMode.UPGRADE_NOWAIT puede UPDATE NOWAIT bajo Oracle.
ser adquirido ante una solicitud explcita del usuario, usando SELECT ... FOR
LockMode.READ es adquirido automticamente cuando Hibernate lee datos bajo los niveles de aislamiento "Repeatable Read" o "Serializable". Puede ser readquirido por solicitud especfica del usuario. LockMode.NONE representa la ausencia de "locks". Todos los objetos pasan a este modo de lock al final de una transaccin. Los objetos asociados con la sesin a travs de un llamado a update() o saveOrUpdate() tambin comienzan en este modo de "lock".
La "solicitud (request) explcita del usuario" se expresa de alguna de las siguientes maneras: Un llamado a Session.load(), especificando un LockMode. Un llamado a Session.lock(). Un llamado a Query.setLockMode(). Si Session.load() es invocado con UPGRADE o UPGRADE_NOWAIT, y el objeto requerido an no haba sido cargado por la sesin, el objeto es cargado usando SELECT ... FOR UPDATE. Si se invoca load() para un objeto que ya est cargado, con un "lock" menos restrictivo que el que se est pidiendo, Hibernate invoca lock() para dicho objeto.
Session.lock() preactica un chequeo del nmero de versin si el "lock" especificado es READ, UPGRADE o UPGRADE_NOWAIT. (En el caso de UPGRADE o UPGRADE_NOWAIT, se usa SELECT ... FOR UPDATE).
Si la base de datos no soporta el lock mode solicitado, Hibernate usar un modo alternativo apropiado (en lugar de emitir una excepcin). Esto asegura que la aplicacin sea porttil.
126 de 198
http://www.hibernar.org/documentacion_es/castellano.html
org.hibernate.ConnectionReleaseMode:
el comportamiento anticuado descrito anteriormente. La sesin de Hibernate obtiene una conexin cuando es necesario efectuar algn acceso a JDBC, y retiene dicha conexin hasta que la sesin se cierre.
AFTER_TRANSACTION: dice
ON_CLOSE: es esencialmente
completado.
AFTER_STATEMENT (tambin conocida
como "liberacin agresiva"): dice que las conexiones se liberen luego de la ejecucin de todos y cada uno de los comandos. Esta liberacin agresiva es omitida si el comando deja recursos abiertos que an estn asociados con la sesin actual; actualmente la nica situacin en la que esto ocurre es cuando se usa un org.hibernate.ScrollableResults.
El parmetro de configuracin hibernate.connection.release_mode se usa para especificar qu modo de liberacin usar. Los valores posibles son:
auto (el valor por defecto): esta opcin le delega la decisin al modo de liberacin recibido por el mtodo org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode(). Con JTATransactionFactory,
devuelve ConnectionReleaseMode.AFTER_STATEMENT; con JDBCTransactionFactory, devuelve ConnectionReleaseMode.AFTER_TRANSACTION. Casi nunca es buena idea cambiar el comportamiento por defecto, porque las fallas en relacin con este valor normalmente indican errores de programacin (bugs) o suposiciones incorrectas en el cdigo.
on_close: indica que se use ConnectionReleaseMode.ON_CLOSE. Este valor se conserva por compatibilidad hacia atrs, pero su uso se desaconseja. after_transaction: indica
que se use ConnectionReleaseMode.AFTER_TRANSACTION. Este valor no debera ser usado en entornos JTA. Tambin note que, con ConnectionReleaseMode.AFTER_TRANSACTION, si una sesin se considera que est en modo auto-commit, las conexiones sern liberadas como si el modo de liberacin fuera AFTER_STATEMENT.
after_statement: indica que se usa ConnectionReleaseMode.AFTER_STATEMENT. Adicionalmente, el ConnectionProvider que est configurado es consultado para comprobar si soporta este valor (supportsAggressiveRelease()). Si no, el modo de liberacin es reinicializado a
ConnectionReleaseMode.AFTER_TRANSACTION. Este valor es seguro slo en entornos en donde se puede o bien recapturar la misma conexin JDBC subyacente cada vez que hacemos un llamado a ConnectionProvider.getConnection(), o bien en entornos con auto-commit en donde no importa si es la misma conexin la que nos es devuelta.
12.1. Interceptores
La interfaz Interceptor provee mtodos de retorno o "callbacks" desde la sesin a la aplicacin, permitindole a la aplicacin inspeccionar y/o manipular propiedades de un objeto persistente antes de grabarlo. Un uso posible de esto es, escribir informacin de seguimiento/auditora. Por ejemplo, el interceptor siguiente asigna automticamente la propiedad createTimestamp cuando se crea un Auditable, y actualiza la propiedad lastUpdateTimestamp cuando un Auditable es actualizado. Se puede o bien implementar Interceptor directamente, o (preferentemente) extender EmptyInterceptor.
package org.hibernate.test; import java.io.Serializable; import java.util.Date; import java.util.Iterator; import org.hibernate.EmptyInterceptor; import org.hibernate.Transaction;
127 de 198
http://www.hibernar.org/documentacion_es/castellano.html
import org.hibernate.type.Type; public class AuditInterceptor extends EmptyInterceptor { private int updates; private int creates; private int loads; public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Ty // no hace nada } public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] pre String[] propertyNames, Type[] types) { if ( entity instanceof Auditable ) { updates++; for ( int i=0; i < propertyNames.length; i++ ) { if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) { currentState[i] = new Date(); return true; } } } return false; } public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, T if ( entity instanceof Auditable ) { loads++; } return false; } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, T if ( entity instanceof Auditable ) { creates++; for ( int i=0; i<propertyNames.length; i++ ) { if ( "createTimestamp".equals( propertyNames[i] ) ) { state[i] = new Date(); return true; } } } return false; } public void afterTransactionCompletion(Transaction tx) { if ( tx.wasCommitted() ) { System.out.println("Creaciones: " + creates + ", Actualizaciones: " + updates, "Cargas: } updates=0; creates=0; loads=0; } }
Los interceptores vienen en dos variantes: Con alcance de sesin (session-scoped), y con alcance de toda la fbrica de sesiones (sessionfactory-scoped). Un interceptor con alcance de sesin se especifica cuando la sesin se abra usando uno de los mtodos adicionales (overloaded) SessionFactory.openSession() que acepta un Interceptor como parmetro.
Session session = sf.openSession( new AuditInterceptor() );
Un interceptor con alcance de SessionFactory se registra con el objeto Configuration antes de consctruir la SessionFactory. En este caos, el interceptor provisto ser a aplicado a todas las sesiones abiertas por esa fbrica de sesiones. Esto es cierto, a menos que se use una sesin que haya sido abierta especificando explcitamente otro interceptor. El cdigo de los interceptores de alcance de SessionFactory debe ser seguro en cuando a acceso concurrente (thread-safe), dado que su cdigo ser (potencialmente) usado por varias sesiones al mismo tiempo. Asegrese de no almacenar estado especfico de una sesin.
new Configuration().setInterceptor( new AuditInterceptor() );
128 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Tambin se necesita una entrada de configuracin, indicndole a Hibernate que use este listener, adems del listener por defecto.
<hibernate-configuration> <session-factory> ... <event type="load"> <listener class="com.eg.MyLoadListener"/> <listener class="org.hibernate.event.def.DefaultLoadEventListener"/> </event> </session-factory> </hibernate-configuration>
Los listeners registrados declarativamente no pueden compartir instancias. Si la misma clase es usada por muchos elementos <listener/>, cada referencia resultar en una instancia separada de esa clase. Se se necesita la capacidad de compartir instancias de listener entre distintos tipos de listener, se debe usar la forma programtica de registrarlos. Por qu impementar una interfaz y tambin definir un tipo especfico durante la configuracin? Porque una implementacin de listener puede estar implementando ms de una interfaz. Al tener que especificar tambin el tipo durante el registro, se vuelve fcil habilitar/inhanilitar los listeners a medida durante la cofiguracin.
129 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La seguridad declarativa de Hibernate usualmente se maneja en una capa de "fachada de sesin" (session faade). A partit de Hibernate3, a algunas acciones se les puede asignar permisos va JACC, y se pueden autorizar va JAAS. sta es una funcionalidad opcional, construida encima de la arquitectura de eventos. Primero se deben configurar los listeners de eventos, para habilitar el uso de autorizaciones JAAS.
<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/> <listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/> <listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/> <listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>
Note que <listener type="..." class="..."/> es simplemente una forma abreviada de <event type="..."> <listener class="..."/></event> para cuando hay exactamente un listener para un tipo de evento en particular. Luego, todava en hibernate.cfg.xml, se vinculan los permisos a roles:
<grant role="admin" entity-name="User" actions="insert,update,read"/> <grant role="su" entity-name="User" actions="*"/>
Los nombres de los roles son aqullos que sean comprendidos por su proveedor JACC.
Esto fallara. provocando un OutOfMemoryException ms o menos alrededor de la lnea nmero 50.0000. Esto se debe a que Hibernate guarda todas las instancias de Customer en el cach de sesin a medida que las va insertando. En este captulo le mostramos cmo evitar este problema. Pero primero, si se est usando procesamiento en lotes (en ingls, "batch processing"), es indispensable habilitar el uso del procesamiento en lotes JDBC, si se pretende alcanzar una performance razonable. Asgnele un valor razonable al tamao del lote JDBC, digamos, de 10 a 50.
hibernate.jdbc.batch_size 20
Note que Hibernate inhabilita la insercin por lotes a nivel de JDBC en forma transparente si se est usando un generador de identificadores identiy. Tal vez se quiera realizar este tipo de trabajo en un proceso en donde la interaccin con el cach de 2do nivel est completamente inhabilitada:
hibernate.cache.use_second_level_cache false
Pero esto no es absolutamente necesario, dado que se puede inhabilitar el CacheMode especficamente, para anular la interaccin con el cach de 2do nivel.
130 de 198
http://www.hibernar.org/documentacion_es/castellano.html
for ( int i=0; i<100000; i++ ) { Customer customer = new Customer(.....); session.save(customer); if ( i % 20 == 0 ) { //20, igual que el tamao del lote JDBC //aplicarle "flush" a un lote de inserts y liberar memoria session.flush(); session.clear(); } } tx.commit(); session.close();
Note que, en este ejemplo de cdigo, las instancias de Customer (cliente) devueltas por la consulta son automticamente desprendidas. Nunca estn asociadas con ningn contexto de persistencia. Se considera que las operaciones insert(), update() y delete() definidas por la interfaz StatelessSession son operaciones directamente a nivel de fila de la base de datos, lo cual resulta en la ejecucin inmediata de un SQL INSERT, UPDATE o DELETE, respectivamente. Por esto, su semntica es muy diferente de las operaciones save(), saveOrUpdate() y delete() definidas por la interfaz Session.
131 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La pseudo-sintaxis para os comandos UPDATE y DELETE es: ( UPDATE | DELETE ) FROM? NombreDeLaEntidad (WHERE Algunos puntos a destacar: en la clusula "from", la palabra FROM es optativa Slo se puede nombrar una entidad en la clusula "from"; opcionalmente, sta puede tener un alias. Si lo tiene, entonces cualquier referencia a propiedades debe estar calificada usando dicho alias. Si la entidad no tiene alias, entonces es ilegal que las propiedades estn calificadas. En estas consultas HQL de "modificacin en masa" no se puede especificar ningn joins (ni implcito, ni explcito). S se pueden usar subconsultas (subqueries) en la clusula "where", y estas subconsultas s pueden contener joins. La clusula "where" tambin es optativa.
A modo de ejemplo: para ejecutar un HQL UPDATE, use el mtodo Query.executeUpdate() (el mtodo se bautiz en honor al mtodo de JDBC PreparedStatement.executeUpdate()):
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName"; // o tambin: String hqlUpdate = "update Customer set name = :newName where name = :oldName"; int updatedEntities = s.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
Por defecto, los comandos UPDATE, no afectan las propiedades version ni timestamp de las entidades involucradas; esto es consistente con la especificacin de EJB3. Sin embargo, se puede forzar a Hibernate a reinicializar adecuadamente los valores de las propiedades version y timestamp usando un a "actualizacin versionada" (versioned update). Esto se logra agregando la palabra VERSIONED luego de UPDATE keyword.
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName"; int updatedEntities = s.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
Note que los tipos de versin a medida (org.hibernate.usertype.UserVersionType) no se permiten en conjuncin con comandos update versioned. Para ejecutar un HQL DELETE, use el mismo mtodo Query.executeUpdate():
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlDelete = "delete Customer c where c.name = :oldName"; // o tambin: String hqlDelete = "delete Customer where name = :oldName"; int deletedEntities = s.createQuery( hqlDelete ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); session.close();
132 de 198
http://www.hibernar.org/documentacion_es/castellano.html
El valor int devuelto por el mtodo Query.executeUpdate() indica el nmero de entidades afectadas por la operacin. Considere que esto puede corresponder o no con el nmero de filas afectadas en la base de datos. Una operacin HQL en masa puede resultar en la ejecucin de mltiples comandos SQL (para las joined-subclass, por ejemplo). El nmero devuelto indica el nmero real de entidades afactadas por el comando. Volviendo al ejemplo de la joined-subclass, un comando DELETE ejecutado contra una de las subclases podra resultar en borrados no slo en la tabla a la cual la subclase est mapeada, sino tambin borrados en la tabla "raz", y potencialmente en otras tablas de joined-subclass ms abajo en la jerarqua de herencias. La pseudo-sintaxis de los comandos INSERT es: INSERT INTO NombreDeLaEntidad lista_de_propiedades comando_select. Algunos puntos a destacar: Slo se soporta la forma INSERT INTO ... SELECT ... ; la forma INSERT INTO ... VALUES ... no. La especificacin de la lista_de_propiedades es anloga a la especificacin de columnas en un comando SQL INSERT. Para entidades que estn involucradas en una herencia mapeada, slo las propiedades que estn definidas directamente en ese nivel de clase en particular pueden ser usadas en la lista_de_propiedades: las propiedades de la superclase no estn permitidas, y las de subclases no tienen sentido. En otras palabras, los comandos INSERT son inherentemente polimrficos. el comando_select puede ser cualquier consulta vlida de seleccin HQL, con la precaucin de que los tipos de retorno deben corresponder con los tipos esperados por el insert. Actualmente, esto es verificado durante la compilacin, en lugar de relegar dicho chequeo a la base de datos. Note, sin embargo, que esto podra causar problemas entre tipos que Hibernate considere o no "equivalentes" ms que "iguales". Por ejemplo, para Hibernate no son iguales los tipos org.hibernate.type.DateType y org.hibernate.type.TimestampType, aunque la base de datos no diferencie entre ambos o sea capaz de manejar la conversin entre ambos. En relacin a la propiedad id, el comando de insercin ofrece dos opciones. Se puede o bien especificarla explcitamente en la lista_de_propiedades (en cuyo caso el valor se toma del comando_select) o se puede omitir de la lista de propiedades (en cuyo caso se usa un valor generado). Esta ltima opcin est disponible solamente cuando se usen generadores de id que operen dentro de la base de datos, intentar usarla con generadores del tipo "residente en memoria" causar una excepcin durante el parsing del comando. Para los efectos de esta discusin, se consideran "generadores residentes en memoria" org.hibernate.id.SequenceGenerator (y sus subclases), y cualquier implementacin de org.hibernate.id.PostInsertIdentifierGenerator. org.hibernate.id.TableHiLoGenerator tampoco se puede usar, porque no expone una manera facitble de obtener sus valores en un comando "select". Para las propiedades mapeadas como version o timestamp, el comando INSERT da dos opciones: se puede o bien especificarlas explcitamente en la lista_de_propiedades (en cuyo caso el valor se toma del comando_select) o se las puede puede omitir de la lista de propiedades (en cuyo caso se usa el valor semilla o "seed value" definido por org.hibernate.type.VersionType). Un ejemplo de ejecucin del comando HQL INSERT:
Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c wh int createdEntities = s.createQuery( hqlInsert ).executeUpdate(); tx.commit(); session.close();
133 de 198
http://www.hibernar.org/documentacion_es/castellano.html
mismo que org.hibernate.eg.Foo, y foo.barSet no es lo mismo que foo.BARSET. Este manual usa minsculas para las palabras HQL. A algunos usuarios les parece que las consultas escritas todas en maysula son ms legibles, pero a nosotros esta convencin nos parece fea cuando est inserta en cdigo Java.
la cual simplemente devuelve todas las instancias de la clase eg.Cat. Normalmente no necesitamos calificar el nombre de la clase de esta manera, dado que el valor por defecto es auto-import. As que casi siempre escribimos:
from Cat
La mayora de las veces har falta asignarle un alias, dado que querremos referirnos a Cat en otras partes de la consulta:
from Cat as cat
Esta consulta le asigna el alias cat a las instancias de Cat, de manera que podamos usar dicho alias ms adelante en la consulta. La palabra as es optativa, tambin podramos escribir:
from Cat cat
Se considera una prctica buena el nombrar los alias en las consultas usando una minscula inicial, en concordancia con los estndares de nombrado para variables locales en Java. (por ejemplo domesticCat).
no es muy til)
Las frases inner join, left outer join and right outer join pueden abreviarse as:
from Cat as cat join cat.mate as mate
134 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Adicionalmente, un join calificado con la palabra "fetch" (captura) permite que las asociaciones o colecciones de valores sean inicializados junto con sus objetos padres, usando un solo SELECT. Esto es particularmente til en el caso de las colecciones; efectivamente reemplaza los "outer joins" y las inicializaciones haraganas (lazy) del archivo de mapeo para asociaciones y colecciones. Vea la Seccin 19.1, Estrategias de captura (fetch) para ms informacin.
from Cat as cat inner join fetch cat.mate left join fetch cat.kittens
A un join del tipo "fetch" normalmente no se le asignan alias, porque los objetos asociados que devuelve no deberan ser usados en la clusula "where", ni en ninguna otra clusula. Adems, los objetos asociados no son devueltos directamente en el resultado de la consulta; en cambio, se puede acceder a ellos a travs del objeto padre. La nica razn por la que se podra necesitar un alias, es si se est usando un join tipo "fetch" recursivo a otra coleccin ms.
from Cat as cat inner join fetch cat.mate left join fetch cat.kittens child left join fetch child.kittens
Note que la construcciones con fetch no deben ser usadas en consultas que luego invoquen iterate() (aunque s se puede con scroll()). Tampoco debera usarse fetch con consultas que usen setMaxResults() o setFirstResult() (los mtodos de paginacin), dado que dichas operaciones se basan en el nmero de filas del resultado, el cual normalmente contendr duplicados debido a esta "captura ansiosa", y por lo tanto la cantidad de filas no ser la que se espera. Tampoco se debe usar fetch junto con condiciones "with" ad hoc. Al efectuar un join tipo "fetch" con ms de una coleccin, es posible crear un producto cartesiano, as que tenga cuidado en este caso. Usar joins "fetch" con mltiples colecciones, adems, da a menudo resultados inesperados con los mapeos de "bag", as que tenga cuidado acerca de cmo formula sus consultas en este caso. Por ltimo, note que las construcciones full join fetch y right join fetch no tienen sentido. Si se est usando captura haragana (lazy fetching) a nivel de las propiedades, con instrumentacin bytecode, es posible forzar a Hibernate para que capture esas propiedades haraganas inmediatamente en la primera consulta, usando fetch all properties.
from Document fetch all properties order by name from Document doc fetch all properties where lower(doc.name) like '%cats%'
135 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La propiedad especial id puede ser usada para referirse a la propiedad indentificadora de una entidad siempre y cuando dicha entidad no haya definido otra propiedad no-identificadora tambin llamada "id". Si la entidad define una propiedad indentificadora con nombre, se puede usar dicho nombre. Las referencias a identificadores compuestos siguen las mismas relgas. Si la entidad tiene una propiedad no-identificadora llamda "id", la propiedad identificadora compuesta slo puede ser referida por el nombre asignado. En caso contrario, se puede usar la propiedad especial id para referirse a la propiedad identificadora. Nota: esto ha cambiado significativamente a partir de la versin 3.2.2. En versiones anteriores, id siempre haca alusin a la propiedad identificadora, sin importar el verdadero nombre. A consecuencia de esto, cuando haba propiedades no-identificadoras llamadas id, las consultas no podan referirse a ellas.
Esta consulta seleccionar los mates (en ingls, "compaeros") de otros Cats. En realidad, se puede expresar esta consulta de una forma ms compacta, como:
select cat.mate from Cat cat
Las consultas pueden devolver propiedades de cualquier tipo de valor, incluidos valores de tipo "componente":
select cat.name from DomesticCat cat where cat.name like 'fri%' select cust.name.firstName from Customer as cust
asumiendo que la clase Family tuviere el constructor apropiado Se les pueden asignar alias a las expresiones seleccionadas usando as:
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n from Cat cat
136 de 198
http://www.hibernar.org/documentacion_es/castellano.html
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
En la clusula "select" se pueden usar operadores aritmticos, concatenacin, y funciones SQL reconocidas:
select cat.weight + sum(kitten.weight) from Cat cat join cat.kittens kitten group by cat.id, cat.weight select firstName||' '||initial||' '||upper(lastName) from Person
Las palabras distinct y all pueden ser usadas, y con la misma semntica que en SQL.
select distinct cat.name from Cat cat select count(distinct cat.name), count(cat) from Cat cat
devuelve no slo instancias de Cat, sino tambin de las subclases como DomesticCat. Las consultas de Hibernate pueden nombrar cualquier clase o interfaz Java en la clusula from. La consulta devolver las instancias de todas las clases persistentes que extiendan o implementen dicha clase o interfaz. La siguiente consuta devuelve todos los objetos persistentes:
from java.lang.Object o
Note que las dos ltimas consultas requerirn ms de dos comandos SQL SELECT. Esto implica que un clusula order by no ordenara correctamente la totalidad del conjunto de resultados, y que no se puede usar Query.scroll() para navegarlos.
137 de 198
http://www.hibernar.org/documentacion_es/castellano.html
devolver las instancias de Foo para las cuales exista una instancia de Bar cuya propiedad date sea igual a la propiedad startDate de Foo. Las expresiones con "path" compuesto hacen que la clusula "where" sea extremadamente poderosa. Considere:
from Cat cat where cat.mate.name is not null
Esta consulta se traducira en un comando SQL con varios (inner) joins. Si se escribiera algo como esto:
from Foo foo where foo.bar.baz.customer.address.city is not null
en SQL se acabara con una consulta que requerira 4 joins de tablas. El operador = puede usarse para comparar no slo propiedades, sino tambin instancias:
from Cat cat, Cat rival where cat.mate = rival.mate select cat, mate from Cat cat, Cat mate where cat.mate = mate
La propiedad id (en minscula), puede usarse para hacer referencia al identificador nico de un objeto. Vase la Seccin 14.5, Referirse a la propiedad identificadora para ms informacin.
from Cat as cat where cat.id = 123 from Cat as cat where cat.mate.id = 69
La segunda consulta es eficiente, no requiere joins. Tambin se pueden usar propiedades de los identificadores compuestos. Supongamos que Person tiviera un identificador compuesto que consistiese en country y medicareNumber. (de nuevo, vase la Seccin 14.5, Referirse a la propiedad identificadora para ms informacin acerca de referirse a las propiedades identificadoras):
from bank.Person person where person.id.country = 'AU' and person.id.medicareNumber = 123456 from bank.Account account where account.owner.id.country = 'AU' and account.owner.id.medicareNumber = 123456
Nuevamente, la segunda consulta no requiere "joins" Del mismo modo, la propiedad especial class accede al valor discriminador de una instancia, en caso de que se est usando persistencia polimrfica. Un nombre de clase de Java incrustado en la clusula "where" ser traducido como su valor de discriminador.
from Cat cat where cat.class = DomesticCat
Tambim se pueden usar componentes, o tipos a medida compuestos, o las propiedades de dichos tipos de componentes/tipos. Vea la Seccin 14.17, Componentes para ms informacin.
138 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Un tipo "any" tiene las propiedades especiales id y class, que permiten expresar un join de la siguiente manera (en donde AuditLog.item es una propiedad mapeada con <any>):
from AuditLog log, Payment payment where log.item.class = 'Payment' and log.item.id = payment.id
Note que, en la consulta precedente, log.item.class y payment.class se estaran refiriendo a valores de columnas completamente diferentes de la base de datos.
14.10. Expresiones
Las expresiones que se permiten en la clusula where incluyen la mayora de las que se podra escribir en SQL: operadores matemticos +, -, *, / operadores de comparacin binaria =, >=, <=, <>, !=, like operaciones lgicas and, or, not parntesis ( ), para indicar agrupamientos
in, not in, between, is null, is not null, is empty, is not empty, member of and not member of
la forma de "case" simple, "Simple" case, case ... when ... then ... else ... end, y la forma de "case" llamada "searched", case when ... then ... else ... end concatencin de cadenas ...||... or concat(...,...)
current_date(), current_time(), current_timestamp() second(...), minute(...), hour(...), day(...), month(...), year(...),
Cualquier funcin u operador definido por EJB-QL 3.0: substring(), trim(), lower(), upper(), length(),
locate(), abs(), sqrt(), bit_length(), mod() coalesce() y nullif() str()
cast(... as ...), en donde el segundo argumento es el nombre de un tipo de Hibernate, y extract(... from ...) si la base de datos subyacente soporta las funciones ANSI cast() y extract().
la funcin HQL index(), que se aplica a los alias de una colecin asociada indexada. funciones HQL que aceptan expresiones tipo "path" con valor de coleccin: size(), minelement(), maxelement(), minindex(), maxindex(), junto con las funciones especiales elements() e indices, las cuales pueden ser cuantificadas usando some, all, exists, any, in. cualqiuer funcin escalar SQL soportada por la base de datos, como sign(), trunc(), rtrim(), sin() parmetros ppsicionales al estilo JDBC parmetros nombrados: :name, :start_date, :x1 expresiones SQL literales 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0' constantes public static final de java eg.Color.RAYADO
in y between
from DomesticCat cat where cat.name between 'A' and 'B' from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )
139 de 198
http://www.hibernar.org/documentacion_es/castellano.html
from DomesticCat cat where cat.name not between 'A' and 'B' from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )
Del mismo modo. is null y is not null pueden ser usadas para chequear valores nulos. Se puede usar fcilmente valores booleanos, declarando sustituciones de consulta HQL en la configuracin de Hibernate:
<property name="hibernate.query.substitutions">true 1, false 0</property>
Esto reemplazar las palabras true y false con los valores literales 1 y 0 en el SQL traducido desde este HQL.
from Cat cat where cat.alive = true
Se puede chequear el tamao de la coleccin con la propiedad especial size, o con la funcin especial size().
from Cat cat where cat.kittens.size > 0 from Cat cat where size(cat.kittens) > 0
Para las colecciones indexadas, es preferible referirse a los valores mnimos y mximos usando las funciones minindex y maxindex. Anlogamente, se puede aludir a los elementos mnimo y mximo de una coleccin de un tipo bsico utilizando las funciones minelement y maxelement.
from Calendar cal where maxelement(cal.holidays) > current_date from Order order where maxindex(order.items) > 100 from Order order where minelement(order.items) > 10000
Las funciones SQL any, some, all, exists, in se soportan cuando se les pasa como parmetro el conjunto de elementos o ndices de una coleccin (stas son las funciones elements e indices), o el resultado de una subconsulta (vase abajo).
select mother from Cat as mother, Cat as kit where kit in elements(foo.kittens) select p from NameList list, Person p where p.name = some elements(list.names) from Cat cat where exists elements(cat.kittens) from Player p where 3 > all elements(p.scores) from Show show where 'fizard' in indices(show.acts)
Note que las construcciones: size, elements, indices, minindex, maxindex, minelement, maxelement slo pueden ser usadas en la clusula "where" en Hibernate3 Uno se puede referir a los elementos en las colecciones indexadas (arrays, lists, maps) por ndice (slo en la clausula "where").
from Order order where order.items[0].id = 1234 select person from Person person, Calendar calendar where calendar.holidays['national day'] = person.birthDay and person.nationality.calendar = calendar select item from Item item, Order order where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11 select item from Item item, Order order
140 de 198
http://www.hibernar.org/documentacion_es/castellano.html
HQL tambin trae una funcin index() ya incorporada, para elementos de una asociacin de-uno-a-muchos, o una coleccin de valores.
select item, index(item) from Order order join order.items item where index(item) < 5
Pueden ser usadas las funciones SQL escalares que la DB subyacente soporte:
from DomesticCat cat where upper(cat.name) like 'FRI%'
Si todo esto an no lo ha convencido, piense cunto ms largo y menos legible habra sido esta consulta en SQL:
select cust from Product prod, Store store inner join store.customers cust where prod.name = 'widget' and store.location.name in ( 'Melbourne', 'Sydney' ) and prod = all elements(cust.currentOrder.lineItems)
Las palabras opcionales asc o desc indican orden ascendente o descendente respectivamente.
141 de 198
http://www.hibernar.org/documentacion_es/castellano.html
select foo.id, avg(name), max(name) from Foo foo join foo.names name group by foo.id
si la base de datos subyacente lo soporta (por ejemplo, MySQL no), dentro de having se permiten funciones SQL, y dentro del order by se permiten funciones agregadas.
select cat from Cat cat join cat.kittens kitten group by cat.id, cat.name, cat.other, cat.properties having avg(kitten.weight) > 100 order by count(kitten) asc, sum(kitten.weight) desc
Note que ni la clusula group by ni la order by pueden contener expresiones aritmticas. Y tambin que Hibernate no "expande" una entidad agrupada: as que no se puede escribir group by cat si todas las propiedades de cat son no-agregadas; hay que listar todas las propiedades no-agregadas explcitamente.
14.13. Subconsultas
Para las base de datos que soporten subconsultas (subqueries), Hibernate soporta subconsultas dentro de una consulta. Una subconsulta debe estar rodeada por parntesis (a menudo por un llamado a una funcin SQL agregada). Incluso se permiten subconsultas correlacionadas (subconsultas que hagan referencia a un alias en la consulta exterior.
from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat ) from DomesticCat as cat where cat.name = some ( select name.nickName from Name as name ) from Cat as cat where not exists ( from Cat as mate where mate.mate = cat ) from DomesticCat as cat where cat.name not in ( select name.nickName from Name as name ) select cat.id, (select max(kit.weight) from cat.kitten kit) from Cat as cat
Note que las subconsultas HQL pueden ocurrir slo en las clusulas "select" o "where". Note que las subconsultas tambin pueden utilizar sintaxis de constructor del valor de fila (row value constructor). Vea la Seccin 14.18, Constructor del valor de fila para ms detalles.
142 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La siguiente consulta devuelve el id de la orden, el nmero de items y el valor total de todas las rdenes impagas por un cliente en particular, y dado un valor total mnimo, ordenando los resultados por valor total. Para determinar los precios, usa el catlogo actual. El SQL resultante, efectuado contra las tablas ORDER, ORDER_LINE, PRODUCT, CATALOG y PRICE tiene 4 "inner joins" y una subconsulta (no correlacionada).
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog.effectiveDate < sysdate and catalog.effectiveDate >= all ( select cat.effectiveDate from Catalog as cat where cat.effectiveDate < sysdate ) group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
Qu monstruo! En realidad, en la vida real no soy muy amigo de las subconsultas, as que mi consulta qued ms bien de esta manera:
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog = :currentCatalog group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
La consulta siguiente cuenta el nmero de pagos en cada estado, exceptuando los pagos que figuren como "aprobacin pendiente" (AWAITING_APPROVAL) en donde el cambio de estado ms reciente haya sido efectuado por el usuario actual. Se traduce en una consulta SQL con 2 inner joins y una subconsulta correlacionada, contra las clases PAYMENT, PAYMENT_STATUS y PAYMENT_STATUS_CHANGE.
select count(payment), status.name from Payment as payment join payment.currentStatus as status join payment.statusChanges as statusChange where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or ( statusChange.timeStamp = ( select max(change.timeStamp) from PaymentStatusChange change where change.payment = payment ) and statusChange.user <> :currentUser ) group by status.name, status.sortOrder order by status.sortOrder
Si yo hubiera mapeado la coleccin statusChanges collection como una lista, la consulta habra sido mucho ms fcil de escribir.
select count(payment), status.name from Payment as payment join payment.currentStatus as status where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser group by status.name, status.sortOrder order by status.sortOrder
143 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La siguiente consulta usa la funcin isNull() de MS SQL Server para devolver todas las cuentas y pagos no efectuados a la organizacin a la cual pertenece el usuario actual. Se traduce en un SQL con tres inner joins, un outer join y un subselect contra las tablas the ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION y ORG_USER.
select account, payment from Account as account left outer join account.payments as payment where :currentUser in elements(account.holder.users) and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID) order by account.type.sortOrder, account.accountNumber, payment.dueDate
Para ordenar un resultado de acuerdo al tamao de las colecciones de hijos, use la consulta siguiente:
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name order by count(msg)
Si su base de datos soporta subselects, se puede poner una condicin en cuanto al tamao de la seleccin, en la clusula "where" de su consulta.
from User usr where size(usr.messages) >= 1
Como esta solucin no puede devolver a un User que tenga 0 mensajes a causa del inner join, la siguiente forma tambin es til:
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name having count(msg) = 0
144 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Los elementos de las colecciones pueden ser ordenados o agrupados usando un filtro de consultas:
Collection orderedCollection = s.filter( collection, "order by this.amount" ); Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );
14.17. Componentes
En HQL, se pueden usar componentes, casi en cualquiera de las mismas formas en que se puede usar "value types" simples. Pueden aparecer en la clusula "select":
select p.name from Person p select p.name.first from Person p
en donde la propiedad "name" de Person es un componente. Los componentes tambin pueden ser usados en clusulas "where":
from Person p where p.name = :name from Person p where p.name.first = :firstName
Esta sintaxis es vlida, aunque un tanto locuaz. Sera bueno poder hacerla un poco ms concisa y usar la sintaxis de "constructor de valor de fila":
from Person p where p.name=('John', 'Jingleheimer-Schmidt')
145 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Otro momento en que la sintaxis de constructor de valor de fila puede ser beneficiosa, es cuando se usen subconsultas que necesiten comparar contra valores mltiples:
from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )
Una cosa a considerar, al decidir si se quiere usar esta sintaxis, es que la consulta ser dependiente del orden de las sub-propiedades del componente en los metadatos.
Hay un buen rango de tipos de "criterion" que ya vienen incluidos (subclases de Restrictions), pero uno en especial permite especificar SQL directamente:
146 de 198
http://www.hibernar.org/documentacion_es/castellano.html
List cats = sess.createCriteria(Cat.class) .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRI .list();
El comodn {alias} ser reemplazado con el alias de fila de la entidad consultada. Un camino alternativo para obtener un "criterion" es obtenerlo a partir de una instancia de Property. Se puede crear una Property invocando Property.forName().
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();
15.4. Asociaciones
Se puede especificar constraints en entidades relacionadas, navegando a travs de asociaciones usando createCriteria().
List cats = sess.createCriteria(Cat.class) .add( Restrictions.like("name", "F%") ) .createCriteria("kittens") .add( Restrictions.like("name", "F%") ) .list();
note que el segundo createCriteria() devuelve una instancia nueva de Criteria, la cual se refiere a los elementos de la coleccin kittens. La siguiente forma alternativa es til en ciertas circunstancias:
List cats = sess.createCriteria(Cat.class) .createAlias("kittens", "kt") .createAlias("mate", "mt") .add( Restrictions.eqProperty("kt.name", "mt.name") ) .list();
(createAlias() no crea una instancia nueva de Criteria.) Note que las colecciones de "kittens" (gatitos) contenidas en las instancias de Cat devueltas por las dos consultas anteriores, no estn pre-filtradas por el Criteria! Si desea obrener slo los "kittens" que cumplen con el Criteria, debe usar un ResultTransformer.
List cats = sess.createCriteria(Cat.class)
147 de 198
http://www.hibernar.org/documentacion_es/castellano.html
.createCriteria("kittens", "kt") .add( Restrictions.eq("name", "F%") ) .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP) .list(); Iterator iter = cats.iterator(); while ( iter.hasNext() ) { Map map = (Map) iter.next(); Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS); Cat kitten = (Cat) map.get("kt"); }
Esta consulta capturar tanto mate como kittens mediante un "outer join". Vea la Seccin 19.1, Estrategias de captura (fetch) para ms informacin.
Las propiedades versin, identificadores y asociaciones son ignoradas. Por defecto, las propiedades con valor nulo son excluidas. Se puede ajustar cmo el Example se aplica:
Example example = Example.create(cat) .excludeZeroes() //excluir las propiedades con valor cero .excludeProperty("color") //excluir una propiedad llamada "color" .ignoreCase() //efectuar comparaciones de cadenas sin importar maysculas/minscul .enableLike(); //usar "like' para las comparaciones de cadenas List results = session.createCriteria(Cat.class) .add(example) .list();
148 de 198
http://www.hibernar.org/documentacion_es/castellano.html
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();
En una consulta de tipo Criteria, no es necesario que haya explcitamente "group by"s. Algunos tipos de proyeccin son las denominadas proyecciones agrupadoras (grouping projections) las cuales aparecen en el SQL como clusulas group by. Optativamente se le puede asignar un alias a la proyeccin, de manera que las Restrictions y los Orders se puedan referir a l. He aqu dos maneras de hacer esto:
List results = session.createCriteria(Cat.class) .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) ) .addOrder( Order.asc("colr") ) .list(); List results = session.createCriteria(Cat.class) .setProjection( Projections.groupProperty("color").as("colr") ) .addOrder( Order.asc("colr") ) .list();
Los mtodos alias() y as() simplemente envuelven una instancia de Projection dentro de otra instancia de Projection con alias. Como atajo, se puede asignar un alias al agregar la proyeccin a una lista de proyecciones:
List results = session.createCriteria(Cat.class) .setProjection( Projections.projectionList() .add( Projections.rowCount(), "catCountByColor" ) .add( Projections.avg("weight"), "avgWeight" ) .add( Projections.max("weight"), "maxWeight" ) .add( Projections.groupProperty("color"), "color" ) ) .addOrder( Order.desc("catCountByColor") ) .addOrder( Order.desc("avgWeight") ) .list(); List results = session.createCriteria(Domestic.class, "cat") .createAlias("kittens", "kit") .setProjection( Projections.projectionList() .add( Projections.property("cat.name"), "catName" ) .add( Projections.property("kit.name"), "kitName" ) ) .addOrder( Order.asc("catName") ) .addOrder( Order.asc("kitName") ) .list();
149 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La clase DetachedCriteria permite crear una consulta por fuera del alcance de una sesin, y ms tarde ejecutarla usando alguna sesin arbitraria.
DetachedCriteria query = DetachedCriteria.forClass(Cat.class) .add( Property.forName("sex").eq('F') ); Session session = ....; Transaction txn = session.beginTransaction(); List results = query.getExecutableCriteria(session).setMaxResults(100).list(); txn.commit(); session.close();
Una DetachedCriteria tambin puede usarse para expresar una subconsulta. Instancias de Criterion que involucren subconsultas pueden ser obtenidas via subconsultas o Property.
DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class) .setProjection( Property.forName("weight").avg() ); session.createCriteria(Cat.class) .add( Property.forName("weight").gt(avgWeight) ) .list(); DetachedCriteria weights = DetachedCriteria.forClass(Cat.class) .setProjection( Property.forName("weight") ); session.createCriteria(Cat.class) .add( Subqueries.geAll("weight", weights) ) .list();
Note que esta funcionalidad no ha sido concebida para usarse con entidades de clave natural mutable. A continuacin, hay que habilitar el cach de consultas de Hibernate. Finalmente, Restrictions.naturalId() permite hacer un uso ms eficiente del algoritmo de cach.
session.createCriteria(User.class) .add( Restrictions.naturalId() .set("name", "gavin")
150 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Estas dos consultas devolvern una List de arrays de Objects (Object[]) con valores escalares para cada columna de la tabla CATS. Hibernate usar ResultSetMetadata para deducir el orden y tipo de los valores escalares devueltos. Para evitar el gasto extra de llamar ResultSetMetadata, o simplemente para se ms explcito acerca de qu se devuelve, se puede usar addScalar().
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME", Hibernate.STRING) .addScalar("BIRTHDATE", Hibernate.DATE);
Esta consulta especific: la cadena SQL de la consulta las columnas y tipos a devolver Esto an devolver un array de Objetcs, pero ahora no usar ResultSetMetadata sino que obtendr explcitamente las columnas ID, NAME and BIRTHDATE como un Long, una String y un Short, respectivamente, a partir del resultado subyacente. Esto tamben significa que slo sern devueltas esas 3 columnas, incluso cuando la consulta est usando * y pueda devolver ms columnas que las 3 listadas. Es posible omitir la informacin de tipo para algunos o todos los escalares:
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME") .addScalar("BIRTHDATE");
sta es esencialmente la misma consulta que antes, pero ahora se usa ResultSetMetaData par decidir el tipo de NAME Y BIRTHDATE, mientras que el tipo de ID se especifica explcitamente. Cmo los java.sql.Types devueltos del ResultSetMetaData se mapean a Hibernate, es controlado por el dialecto. Si un tipo en particular no est mapeado o no se resuelve en el tipo esperado, es posible modificarlo a medida, usando llamados a registerHibernateType en el dialecto.
151 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Las consultas precedentes consistan todas en devolver valores escalares, bsicamente devolviendo los valores "crudos" desede el resultset. A continuacin se muestra cmo obtener objetos de entidad desde una consulta nativa, a travs de addEntity().
sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class); sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
Esta consulta especific: la cadena SQL de la consulta la entidad devuelta por la consulta Suponiendo que Cat est mapeado como clase, con las columnas ID, NAME y BIRTHDATE, las 2 consultas precedentes devolvern una List en la cual cada elemento es una entidad Cat. Si la entidad est mapeada a otra entidad con una asociacin many-to-one (de-muchos-a-uno), es necesario devolver tambin eso al efectuar la consulta nativa, de otro modo ocurrir un error especfico de base de datos del tipo "columna no encontrada". Las columnas adicionales sern devueltas automticamente cuando se use la notacin *, pero se prefiere la forma explcita, como en el siguiente ejemplo en donde tenemos una relacin 'de-muchos-a-uno' con un Dog:
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
En este ejemplo, los Cats devueltos tendrn su propiedad dog enteramente inicializada, sin necesitad de un viaje adicional de ida y vuelta a la BD. Note que hemos agregado un nombre de alias ("cat") para poder especificar la propiedad de destino del join. Es posible hacer este mismo tipo de join "vido" para colecciones, por ejemplo, si el Cat tuviera varios Dogs asociados de-uno-a-muchos.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.I .addEntity("cat", Cat.class) .addJoin("cat.dogs");
Llegados a este punto, casi hemos alcanzado el lmite de lo que se puede hacer con consultas nativas, antes de empezar a mejorar su SQL para que sean usables en Hibernate. El problema comienza cuando se devuelven mltiples entidades del mismo tipo, o cuando los alias/nombres de columna empleados por defecto no son suficientes.
Lo que se intenta es que esta consulta devuelva dos instancias de Cat por fila: un gato y su madre. Esto fallar, dado que hay un conflicto de nombres, porque estn mapeados a los mismos nombres de columna, y, en algunas bases de datos, los alias de columna devueltos muy probablemente tendrn la forma "c.ID", "c.NAME", etc., lo cual no coincide con las
152 de 198
http://www.hibernar.org/documentacion_es/castellano.html
columnas especificadas en el mapeo ("ID" y "NAME"). La forma siguiente no es vulnerable a esta duplicacin del nombre de columna:
sess.createSQLQuery("SELECT {cat.*}, {mother.*} .addEntity("cat", Cat.class) .addEntity("mother", Cat.class); FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
Esta consulta especific: la cadena de la consulta SQL, con parmetros de sustitucin (placeholders) para que hibernate inyecte los alias de columna las entidades devueltas por la consulta La notacin con {cat.*} y {mother.*} usada anteriormente es taquigrafa por "todas las propiedades". Alternativamente, se puede listar las columnas explcitamente, pero incluso en ese caso dejamos que Hibernate inyecte los alias de columna SQL para cada propiedad. El parmetro de sustitucin para un alias de columna es simplemente el nombre de la propiedad calificado por el alias de la tabla. En el ejemplo siguiente, obtenemos los Cats (gatos) y sus madres desde una tabla diferente (cat_log) a la declarada en los metadatos de mapeo. Note que incluso se puede usar los alias de propiedad en la clusula "where" si se quiere.
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " + "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " + "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID"; List loggedCats = sess.createSQLQuery(sql) .addEntity("cat", Cat.class) .addEntity("mother", Cat.class).list();
Ejemplo
A_NAME as {item.name} CURRENCY as {item.amount.currency}, VALUE as {item.amount.value} DISC as {item.class}
NAME as {coll.element.name}
{coll.element.*}
153 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Sintaxis
{[aliasname].*} {coll.*}
Ejemplo
La consulta especific: la cadena SQL de la consulta un transformador de resultado (result transformer) La consulta precedente devolver una lista de CatDTO, la cual habr sido instanciada y habr inyectado los valores de NAME y BIRTHNAME en las propiedades o campos correspondientes.
16.1.7. Parmetros
Las consultas SQL nativas soportan parmetros tanto nombrados como posicionales:
Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class); List pusList = query.setString(0, "Pus%").list(); query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class); List pusList = query.setString("name", "Pus%").list();
Los elementos <return-join> y <load-collection> se usan, respectivamente, para asociaciones de tipo "join", y para definir consultas que inicialicen colecciones.
<sql-query name="personsWith"> <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/>
154 de 198
http://www.hibernar.org/documentacion_es/castellano.html
SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, address.STREET AS {address.street}, address.CITY AS {address.city}, address.STATE AS {address.state}, address.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS address ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>
Una consulta SQL nombrada puede devolver un valor escalar. Hya que declarar el alias de la columna y el tipo de Hibernate usando el elemento <return-scalar> element:
<sql-query name="mySqlQuery"> <return-scalar column="name" type="string"/> <return-scalar column="age" type="long"/> SELECT p.NAME AS name, p.AGE AS age, FROM PERSON p WHERE p.NAME LIKE 'Hiber%' </sql-query>
Se puede externalizar el resultado, mapeando informacin en un elemento <resultset> para reutilizarlo, ya sea en otras consultas nombradas, o a travs de la API setResultSetMapping().
<resultset name="personAddress"> <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/> </resultset> <sql-query name="personsWith" resultset-ref="personAddress"> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, address.STREET AS {address.street}, address.CITY AS {address.city}, address.STATE AS {address.state}, address.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS address ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>
Alternativamente, se puede usar la informacin de mapeo del resultset contenida en los archivos hbm, directamente en el cdigo Java.
List cats = sess.createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) .setResultSetMapping("catAndKitten") .list();
155 de 198
http://www.hibernar.org/documentacion_es/castellano.html
person.AGE AS myAge, person.SEX AS mySex, FROM PERSON person WHERE person.NAME LIKE :name </sql-query> <return-property> tambin funciona con mltiples columnas. Esto soluciona la limitacin de la sintaxis {}, la cual no permite un control tan minucioso de las propiedades multicolumna. <sql-query name="organizationCurrentEmployments"> <return alias="emp" class="Employment"> <return-property name="salary"> <return-column name="VALUE"/> <return-column name="CURRENCY"/> </return-property> <return-property name="endDate" column="myEndDate"/> </return> SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate}, REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY FROM EMPLOYMENT WHERE EMPLOYER = :id AND ENDDATE IS NULL ORDER BY STARTDATE ASC </sql-query>
Note que en este ejemplo utilizamos <return-property> en combinacin con la sintaxis {} para inyectar, permitindole al usuario decidir cmo prefiere referirse a columnas y propiedades. Si su mapeo tiene un discriminador, debe usarse <return-discriminator> para especificar la columna discriminadora.
Para usar esta consulta en Hibernate, hay que mapearla con una consulta "nombrada".
<sql-query name="selectAllEmployees_SP" callable="true"> <return alias="emp" class="Employment"> <return-property name="employee" column="EMPLOYEE"/> <return-property <return-property <return-property <return-property <return-property <return-property name="employer" column="EMPLOYER"/> name="startDate" column="STARTDATE"/> name="endDate" column="ENDDATE"/> name="regionCode" column="REGIONCODE"/> name="id" column="EID"/> name="salary">
156 de 198
http://www.hibernar.org/documentacion_es/castellano.html
collection>
Note que los procedimientos almacenados actualmente slo devuelven escalares o entidades. <return-join> y <loadno se soportan.
El SQL es ejecutado directamente en su base de datos, as que usted es libre de utilizar cualquier dialecto que desee. Por supuesto, usar SQL especfico de una base de datos reducir la "portabilidad" de su aplicacin. Los procedimientos almacenados se soportan, si se configura el atributo callable:
<class name="Person"> <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert> <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete> <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update> </class>
157 de 198
http://www.hibernar.org/documentacion_es/castellano.html
El orden de los parmetros posicionales es ahora esencial, dado que deben estar en la misma secuencia en que Hibernate los espera. Se puede ver el orden esperado, habilitando logueo a nivel "debug" en org.hibernate.persister.entity. Con esta categora habilitada,, Hibernate imprimir el SQL esttico que se usa para crear entidades, actualizarlas, borrarlas, etc. (Para ver la secuencia esperada, recuerde no incluir su SQL a medida en los archivos de mapeo, dado que ste suplantar al SQL esttico generado por Hibernate). En la mayora de los casos, es obligatorio (o ms bien, fuertemente recomendado) que los procedimientos almacenados devuelvan el nmero de filas insertadas/actualizadas/borradas, porque Hibernate realiza algunos chequeos en tiempo de ejecucin para verificar el xito del comando. Hibernate siempre registra el primer parmetro del "statement" como de salida y numrico, para las operaciones de ABM (alta-baja-modificacin):
CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) RETURN NUMBER IS BEGIN update PERSON set NAME = uname, where ID = uid; return SQL%ROWCOUNT; END updatePerson;
Esta es, simplemente, una declaracin de una consulta nombrada (named query), como se discuti anteriormente. Se puede hacer referencia a esta consulta nombrada en el mapeo de clases:
<class name="Person"> <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <loader query-ref="person"/> </class>
Esto funciona incluso con procedimientos almacenados. Se puede incluso definir un a consulta para cargar la coleccin:
<set name="employments" inverse="true"> <key/> <one-to-many class="Employment"/> <loader query-ref="employments"/> </set> <sql-query name="employments"> <load-collection alias="emp" role="Person.employments"/> SELECT {emp.*} FROM EMPLOYMENT emp WHERE EMPLOYER = :id ORDER BY STARTDATE ASC, EMPLOYEE ASC </sql-query>
158 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Hasta se puede definir un "cargador de entidades" que cargue una coleccn mediante captura con "join":
<sql-query name="person"> <return alias="pers" class="Person"/> <return-join alias="emp" property="pers.employments"/> SELECT NAME AS {pers.*}, {emp.*} FROM PERSON pers LEFT OUTER JOIN EMPLOYMENT emp ON pers.ID = emp.PERSON_ID WHERE ID=? </sql-query>
o a una coleccin:
<set ...> <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/> </set>
o incluso a ambas, o a varias de ambas, al mismo tiempo. Los mtodos en Session son: enableFilter(String filterName), getEnabledFilter(String filterName), y disableFilter(String filterName). Por defecto, los filtros no estn habilitados para una sesin dada; deben ser habilitados explcitamente mediante el uso del mtodo Session.enableFilter(), el cual devuelve una instancia de la interfaz Filter. Usando el filtro simple definido antes, esto se vera as:
session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
Note que hay mtodos en la interfaz org.hibernate.Filter que permiten el encadenamiento de filtros, muy comn en Hibernate. Un ejemplo completo, usando datos con el patrn de programacin conocido como "fecha efectiva" (effective date):
<filter-def name="effectiveDate"> <filter-param name="asOfDate" type="date"/>
159 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</filter-def> <class name="Employee" ...> ... <many-to-one name="department" column="dept_id" class="Department"/> <property name="effectiveStartDate" type="date" column="eff_start_dt"/> <property name="effectiveEndDate" type="date" column="eff_end_dt"/> ... <!-Note que esto asume que los registros no-terminales tinenen una fecha de finalizacin efect a la cual se le asign el mximo valor de fecha posible en la BD, para simplificar. --> <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/> </class> <class name="Department" ...> ... <set name="employees" lazy="true"> <key column="dept_id"/> <one-to-many class="Employee"/> <filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/> </set> </class>
Luego, para asegurarse de que siempre se obtiene el registro ms actual, simplemente habilite el filtro en la sesin, antes de capturar datos de empleados:
Session session = ...; session.enableFilter("effectiveDate").setParameter("asOfDate", new Date()); List results = session.createQuery("from Employee as e where e.salary > :targetSalary") .setLong("targetSalary", new Long(1000000)) .list();
En el HQL precedente, incluso si slo se mencion una condicin relativa al salario en los resultados, la consulta devolver los empleados que con un salario mayor a un milln y, a causa del filtro habilitado, que estn actualmente activos. Nota: si se planea usar filtros con "outer joins" (sea a travs de HQL o de estrategias de "fetch") tenga cuidado con la direccin de la expresin de condicin. Lo ms seguro es configurarlo como "left outer join"; en general, ponga el parmetro primero, seguido por el nombre o nombres de columna luego del operador. Luego de haber sido definido, un filtro puede ser adosado a entidades y/o colecciones, cada una con su propia condicin. Eso puede ser tedioso cuando las condiciones son las mismas en todos los casos. Por esto, <filter-def/> permite definir una condicin por defecto, sea como un atributo, o como CDATA:
<filter-def name="myFilter" condition="abc > xyz">...</filter-def> <filter-def name="myOtherFilter">abc=xyz</filter-def>
Entonces, esta condicin por defecto ser usada siempre que el filtro se adjunte a algo sin haber especificado una condicin. Note que esto significa que se puede dar una condicin especfica como parte de la adjuncin del filtro, que suplanta a la condicin por defecto para ese caso en particular.
160 de 198
http://www.hibernar.org/documentacion_es/castellano.html
desde la base de datos, y lograr que cualquier modificacin practicada al XML se sincronice automticamente a la base de datos. Incluso se puede tomar un documento XML, analizarlo usando dom4j, y escribirlo en la base de datos con cualquiera de las operaciones bsicas de Hibernate: persist(), saveOrUpdate(), merge(), delete(), replicate() (merge an no se soporta). Esta habilidad tiene muchos usos, incluyendo: importacin/exportacin de datos, externalizacin de entidades via JMS o SOAP, y produccin de reportes basada en XSLT. Un solo mapeo puede ser usado para mapear simultneamente las propiedades de una clase y los nodos de un documento XML a la base de datos, o, si no hay ninguna clase para mapear, slo el documento XML.
Este mapeo permite acceder a los datos como si fueran un rbol dom4j, o una representacin en forma de pares nombre/valor (Maps de Java). Los nombres de las propiedades son construcciones puramente lgicas, a las que se puede hacer referencia en consultas HQL.
"element-name/@attribute-name": mapea
Para asociaciones y asociaciones a valores simples, hay un atributo embed-xml adicional. Si se especifica embedes el valor por defecto, el rbol XML para la entidad asociada (la de tipo coleccin o "value type") ser incrustada directamente en el rbol XML de la entidad que posee la asociacin. En caso contrario, si embedxml="false", entonces slo el valor del identificador al que se hace referencia aparecer en el XML (para asociaciones
xml="true", lo cua
161 de 198
http://www.hibernar.org/documentacion_es/castellano.html
de "value type"s) y para las colecciones no aparecer nada. Debera tenerse cuidado de no asignarles embed-xml="true" a demasiadas asociaciones, dado que XML no sabe lidiar muy bien con recursividad!
<class name="Customer" table="CUSTOMER" node="customer"> <id name="id" column="CUST_ID" node="@id"/> <map name="accounts" node="." embed-xml="true"> <key column="CUSTOMER_ID" not-null="true"/> <map-key column="SHORT_DESC" node="@short-desc" type="string"/> <one-to-many entity-name="Account" embed-xml="false" node="account"/> </map> <component name="name" node="name"> <property name="firstName" node="first-name"/> <property name="initial" node="initial"/> <property name="lastName" node="last-name"/> </component> ... </class>
en este caso, hemos decidido incrustar una coleccin de ids de cuentas (accounts), pero no los datos de las cuentas propiamente dichas. La siguiente consulta HQL:
from Customer c left join fetch c.accounts where c.lastName like :lastName
162 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Vamos a leer y actualizar documentos XML en la aplicacin. Lo hacemos obteniendo una sesin dom4j:
Document doc = ....; Session session = factory.openSession(); Session dom4jSession = session.getSession(EntityMode.DOM4J); Transaction tx = session.beginTransaction(); List results = dom4jSession .createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName") .list(); for ( int i=0; i<results.size(); i++ ) { //le agrega los datos del cliente (customer) al documento XML Element customer = (Element) results.get(i); doc.add(customer); } tx.commit(); session.close(); Session session = factory.openSession(); Session dom4jSession = session.getSession(EntityMode.DOM4J); Transaction tx = session.beginTransaction(); Element cust = (Element) dom4jSession.get("Customer", customerId); for ( int i=0; i<results.size(); i++ ) { Element customer = (Element) results.get(i); //cambia el nombre del cliente en el XML y en la BD Element name = customer.element("name"); name.element("first-name").setText(firstName); name.element("initial").setText(initial); name.element("last-name").setText(lastName); } tx.commit(); session.close();
Combinar esta funcionalidad con la operacin replicate() es extremadamente til para implementar la importacin/exportacin de datos basados en XML:
163 de 198
http://www.hibernar.org/documentacion_es/castellano.html
captura haragana de colecciones: una coleccin es capturada slo cuando la aplicacn invoca alguna operacin en esa coleccin (ste es el comportamiento por defecto para las colecciones). captura "sper-haragana" de colecciones (extra-lazy collection fetching): se accede a elementos individuales de la coleccin desde la base de datos, slo en la medida en que se necesita. Hibernate no intenta capturar toda la coleccin en memoria, a menos que sea absolutamente necesario (esto es lo adecuado para colecciones muy extensas). captura por Proxy: una asociacin de un solo valor se captura cuando se invoca sobre ste cualquier otro mtodo que no sea su getter/setter. captura sin proxy: una asociacin de un solo valor se captura cuando se accede a la variable de instancia . En comparacin con la captura por proxy, este abordaje es menos haragn (se accede a la asociacin incluso si slo se usa el identificador), pero ms transparente, dado que no hay ningn proxy visible para la aplicacn. Este abordaje requiere instrumentacin bytecode de tiempo de compilacin, y slo raramente es necesario. captura haragana de atributos: un atributo o asociacin de valor simple, se capturan cuando se accede a la instancia o asociacin. Este abordaje requiere instrumentacin bytecode de tiempo de compilacin, y slo raramente es necesario. Aqu tenemos dos nociones ortogonales: cundo la asociacin se captura, y cmo se captura (qu SQL se usa). A no confundir una con la otra! Se usa la palabra fetch para ajustar la eprformance. Se puede usar la palabra lazy para definir un contrato sobre qu estar disponible y qu estar desprendido para una clase en particular.
Como la coleccin "permissions" no estaba inicializada cuando la sesin fue cerrada, la coleccin ser incapaz de cargar su estado. Hibernate no soporta la inicializacin haragana de objetos desprendidos. La solucin es mover el cdigo que lee de la coleccin a justo antes de que la transaccin invoque "commit". Alternativamente, podramos usar una coleccin o asociacin no haragana, especificando lazy="false" para el mapeo de la asociacin. De todos modos, se espera que se use inicializacin haragana en casi todos los casos. Si se definen demasiadas asociaciones no haraganas, Hibernate terminar cargando toda la base de datos en memoria para cada transaccin! Por otra parte, a menudo querremos elegir captura por join (la cual que es naturalmente no haragana) en lugar de captura por select, para una transaccin en particular. Ahora veremos cmo disear una estrategia de captura a medida. En Hibernate3, los mecanismos para elegir una estrategia de captura son idnticos para las asociaciones de valor simple y para las colecciones.
164 de 198
http://www.hibernar.org/documentacion_es/castellano.html
As que tal vez querramos habilitar la captura por join en el documento de mapeo.
<set name="permissions" fetch="join"> <key column="userId"/> <one-to-many class="Permission"/> </set <many-to-one name="mother" class="Cat" fetch="join"/>
La estrategia de captura (fetch) definida en el documentod e mapeo afecta a: la obtencin de datos via get() o load() la obtencin de datos que ocurre implcitamente cuando se navega la asociacin las consultas Criteria las consultas HQL (si se usa la captura por subselect ) Sin importar qu estrategia de captura se use, se garantiza que el rbol "no haragn" definido se cargar en memoria. Note que esto puede resultar en que se usen varios SELECTs inmediatamente para ejecutar una consulta HQL en particular. Normalmente no usaremos el documento de mapeo para ajustar el tipo de captura. En cambio, mentendremos el comportamiento por defecto, y lo sustituiremos para una transaccin en particular, usando left join fetch en HQL. Esto le dice a Hibernate que capture la asociacn en forma ansiosa, en el primer SELECT, usando un outer join. En la API de consultas Criteria, se usara setFetchMode(FetchMode.JOIN).
Criteria, por
Si se deseare cambiar la estrategia de captura usada por get() or load(), simplemente habra que usar una consulta ejemplo:
User user = (User) session.createCriteria(User.class) .setFetchMode("permissions", FetchMode.JOIN) .add( Restrictions.idEq(userId) ) .uniqueResult();
(ste es el equivalente en Hibernate de lo que algunas soluciones ORM llaman un plan de captura o "fetch plan"). Una forma completamente distinta de evitar los problemas con N+1 selects es usar el cach de 2do nivel.
165 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Primero que nada, las instancias de la clase Cat jams podrn ser convertidas con "cast" a DomesticCat, incluso cuando la instancia subyacente s es una instancia de DomesticCat:
Cat cat = (Cat) session.load(Cat.class, id); if ( cat.isDomesticCat() ) { DomesticCat dc = (DomesticCat) cat; .... } // instancia un proxy (no llama a la base de datos) // llama a la base de datos para inicializar el proxy // Error!
Sin embargo, esta situacin no es tan mala como parece. Incluso si ahora tenemos dos referencias a objetos "proxy" diferentes, la instancia subyacente an es el mismo objeto:
cat.setWeight(11.0); // llama a la base de datos para inicializar el proxy System.out.println( dc.getWeight() ); // 11.0
En tercer lugar, no se puede usar un proxy CGLIB para clases final ni para clases que tengan mtodos final. Por ltimo, si su clase persistente adquiere recursos durante la inicializacin (por ejemplo, en inicializadores o constructores por defecto), entonces dichos recursos sern tambin adquiridos por el proxy. La clase proxy es una verdadera subclase de la clase persistente. Estos problemas se deben todos a limitaciones fundamentales del modelo de herencia nica de Java. Si usted quiere evitar estos problemas, cada una de sus clases persistentes debe implementar una interfaz que declare sus mtodos de negocio. Y usted debe especificar estas interfaces en el documento de mapeo. Por ejemplo:
<class name="CatImpl" proxy="Cat"> ... <subclass name="DomesticCatImpl" proxy="DomesticCat"> ... </subclass> </class>
en donde CatImpl implementa la interfaz Cat y DomesticCatImpl implementa la interfaz DomesticCat. As, los proxies para instancias de Cat y DomesticCat pueden ser devueltos por load() o por iterate(). (Note que list() normalmente no devuelve proxies.)
Cat cat = (Cat) session.load(CatImpl.class, catid); Iterator iter = session.createQuery("from CatImpl as cat where cat.name='fritz'").iterate(); Cat fritz = (Cat) iter.next();
Las relaciones tambin son incializadas en forma haragana. Esto significa que toda propiedad debe ser declarada como de tipo Cat, no CatImpl. Ciertas operaciones no requieren inicializacin de proxies:
equals(),
hashCode(),
el getter del mtodo identificador Hibernate detectar las clases persistentes que sustituyan (override) equals() o hashCode(). Eligiendo lazy="no-proxy" en lugar del lazy="proxy" que viene por defecto, podemos evitar los problemas asociados con la conversin ("cast") entre tipos. Sin embargo, necesitaremos instrumentacin de bytecode en tiempo de compilacin, y todas las operaciones resultarn en una inicializacin de proxy inmediata.
166 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Si se trata de acceder a una coleccin o proxy no inicializados fuera del alcance de una sesin, Hibernate emitir una LazyInitializationException. Es decir, cuando la entidad que posea la coleccin o que tienga una referencia al proxy est en un estado desprendido. A veces necesitamos asegurarnos de que un proxy o coleccin sean inicializados antes de cerrar la sesin. Por supuesto, siempre podemos forzar la inicializacin invocando cat.getSex() o cat.getKittens().size(), por ejemplo. Pero esto sera confuso para quien leyere el cdigo, e inconveniente para cualquier cdigo que intentare ser genrico. Los mtodos estticos Hibernate.initialize() e Hibernate.isInitialized() le proveen a la aplicacin una forma conveniente de trabajar con colecciones o proxies que hayan sido inicializados en forma haragana. Hibernate.initialize(cat) forzar la inicializacin del proxy, cat, siempre y cuando su sesin est an abierta. Hibernate.initialize( cat.getKittens() ) tiene un efecto similar para la coleccin de kittens (gatitos). Otra opcin es mantener la sesin abierta hasta que todas las colecciones y proxies necesarios hayan sido cargados. En la arquitectura de algunas aplicaciones, particularmente en donde el cdigo que accede a Hibernate y el cdigo que lo usa estn en distinas capas, o en distintos procesos fsicos, asegurarse de que una nica sesin se mantenga abierta cuando una coleccin se inicialice puede llegar a ser un problema. Hay bsicamente dos maneras de lidiar con esto: En una aplicacin de web, se puede usar un filtro de servlet para cerrar la sesin slo cuando realmente se llegue al final de la request, una vez que la presentacin de la interfaz de usuario (view) ha sido completada (el patrn de programacin Open Session in View ). Por supuesto, esto crea una pesada responsabilidad en cuando a la correccin del manejo de excepciones por parte de la infraestructura de la aplicacin. Es de vital importancia que la sesin sea cerrada y la transaccin concluida antes de retornar el control al usuario, incluso cuando ocurriere una excepcin mientras se presentare la interfaz de usuario. Vea la wiki de Hibernate para ejemplos de este patrn "Open Session in View". En aplicaciones que tengan una capa de negocios separada, la lgica de negocio debe "preparar" todas las colecciones que vayan a ser necesitadas por la capa de web, antes de retornar. Esto significa que la capa de negocios debera cargar todos los datos que hagan falta para un caso en particular, y devolvrselos a la capa de interfaz de usuario/web. Normalmente, la aplicacin llama a Hibernate.initialize() para cada coleccin que vaya a ser necesaria en la capa de web (este llamado ocurre antes de que la sesin se cierre), u obtiene la coleccin en forma ansiosa usando una consulta de Hibernate con clusula FETCH o con un FetchMode.JOIN en el Criteria. Esto es normalmente ms fcil si se adopta el patrn de programacin Command en lugar del patrn Session Faade. Tambin se puede adjuntar un objeto previamente cargado a una nueva sesin, con merge() o lock() antes de acceder a colecciones (u otros proxies) no inicializados. No, Hibernate no debera hacer esto automticamente, puesto que ello introducira una semntica de transaccion ad hoc! A veces usted no querr inicializar toda una coleccin muy extensa, pero s necesitar un poco de informacin acerca de ella (por ejemplo, su tamao), o un subconjunto de sus datos. Se puede usar un filtro de coleccin para obtener el tamao de una coleccin sin inicializarla:
( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()
El mtodo createFilter() se usa tambin para obtener eficientemente subconjuntos de una coleccin sin necesidad de inicializarla toda.
s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();
167 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Hibernate ahora ejecutar slo 3 consultas: el patrn es 10, 10, 5. Tambin se puede habilitar la captura en lotes de colecciones. Por ejemplo, si cada Person tiene un coleccin haragana de Cats, y en un momento hay 10 personas cargadas en la sesin, iterar a travs de todas esas personas generara 10 SELECTs, uno por cada llamado a getCats(). Si se habilita la captura por lotes para las colecciones de cats en el mapeo de Person, Hibernate pre-capturar las colecciones:
<class name="Person"> <set name="cats" batch-size="3"> ... </set> </class>
Con un a batch-size igual a 3, Hibernate cargar 3, 3, 3, 1 colecciones en 4 SELECTs. De nuevo, el valor del atributo depender del nmero esperado de colecciones no inicializadas para una sesin en particular. La captura por lotes es particularmente til si se tiene un rbol anidado de items, por ejemplo, la tpica "Lista de Materiales" necesarios para construir una pieza (bill of Materials o BOM, por sus siglas en ingls). (Aunque para rboles de "mayormente lectura" un set anidado o un "path materialziado" seran mejores opciones) .
La captura haragana de propiedades requiere instrumentacin bytecode de tiempo de compilacin! Si sus clases persistentes han sido mejoradas, Hibernate ignorar silenciosamente la configuracin de propiedades a "lazy", y se resignar a usar la captura inmediata usual. Para la instrumentacin bytecode, use la siguiente tarea de Ant:
<target name="instrument" depends="compile"> <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask"> <classpath path="${jar.path}"/> <classpath path="${classes.dir}"/> <classpath refid="lib.class.path"/> </taskdef> <instrument verbose="true"> <fileset dir="${testclasses.dir}/org/hibernate/auction/model"> <include name="*.class"/>
168 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Una manera diferente (y tal vez mejor) de evitar lecturas de columa innecesarias, al menos en transacciones de slo-lectura, es usar las capacidades de proyeccin (Projections) que tienen las consultas HQL y Criteria. Esto evita la necesidad del bytecode de tiempo de compiacin, y es ciertamente una solucin preferible. Se puede forzar la captura ansiosa usual de propiedades, usando fetch all properties en HQL.
Cach
Tipo
Hashtable (no se espera org.hibernate.cache.HashtableCacheProvider que sea usado en produccin) EHCache OSCache
org.hibernate.cache.EhCacheProvider org.hibernate.cache.OSCacheProvider
memoria
memoria, disco memoria, disco en s cluster(multicast (invalidacin de ip) de cluster) en cluster (multicast de ip), transaccional
s s
SwarmCache org.hibernate.cache.SwarmCacheProvider
169 de 198
http://www.hibernar.org/documentacion_es/castellano.html
/>
(1)
usage (obligatorio) especifica read-only
nivel.
Alternativamente (y tal vez preferiblemente) se puede especificar elementos <class-cache> y <collection-cache> en el archivo hibernate.cfg.xml. EL atributo usage especifica una estrategia de concurrencia de cach.
170 de 198
http://www.hibernar.org/documentacion_es/castellano.html
La estrategia transaccional (transactional) provee soporte para proveedores de cach enteramente transaccionales, como JBoss TreeCache. Tales cachs slo pueden ser usados en un entorno JTA, y se debe especificar hibernate.transaction.manager_lookup_class.
La Session tambin provee un mtodo contains() para determinar si una instancia ya pertenece al cach de sesin. Para desalojar a todos los objetos del cach de sesin, llame Session.clear() Para el cach de 2do nivel, hay mtodos definidos en SessionFactory para desalojar el estado cacheado de una instancia, de toda una clase, de la instancia de una coleccin, o de un rol de coleccin completo.
sessionFactory.evict(Cat.class, catId); //desaloja una instancia de Cat en particular sessionFactory.evict(Cat.class); //desaloja todos los Cats sessionFactory.evictCollection("Cat.kittens", catId); //desaloja un coleccin de kittens en particu sessionFactory.evictCollection("Cat.kittens"); //desaloja todas lsa colecciones de kittens
El argumento CacheMode controla cmo una sesin en particular interacta con el cach de 2do nivel.
CacheMode.NORMAL: lee
171 de 198
http://www.hibernar.org/documentacion_es/castellano.html
CacheMode.GET: lee
items del cach de 2do nivel, pero no escribe en el cach de 2do nivel excepto cuando se items en el cach de 2do nivel, pero no lee.
actualicen datos.
CacheMode.PUT:escribe
items en el cach de 2do nivel, pero lee del cach de 2do nivel, elude los efectos de do hibernate.cache.use_minimal_puts, forzando un refresco del cach de 2 nivel para todos los items ledos de la base de datos.
CacheMode.REFRESH: escribe
Para navegar por ls contenidos del cach de 2do nivel, o de una regin de cacheo de consultas, use la API de Statistics:
Map cacheEntries = sessionFactory.getStatistics() .getSecondLevelCacheStatistics(regionName) .getEntries();
Necesitar habilitar estadsticas, y, optativamente, forzar a Hibernate a que escriba las entradas de cach en un formato ms legible por seres humanos:
hibernate.generate_statistics true hibernate.cache.use_structured_entries true
Esta configuracin provoca la creacin de dos nuevas regiones de cach: una que contendr los resultados cacheados de las consultas (org.hibernate.cache.StandardQueryCache), y la otra conteniendo la fecha y hora de las actualizaciones ms recientes hechas en las tablas "consultables" (org.hibernate.cache.UpdateTimestampsCache). Note que el cach de consultas no cachea el estado de las entidades mismas contenidas en el resultado; cachea solamente los valores de los identificadores, y los resultados de tipo "value type". As que el cach de consultas siempre debera ser usado en conjuncin con el cach de 2do nivel. A la mayora de las consultas no les representa ninguna ventaja el ser cacheadas, as que las consultas no se cachean por defecto. Par habilitar el cacheo, invoque Query.setCacheable(true). Este llamado permite que la consulta, al ser ejecutada, busque resultados ya existentes, o agregue sus resultados al cach. Si se require un control ms granular sobre las polticas de expiracin de los cachs, se puede especificar una regin de cach para una consulta en particular, invocando Query.setCacheRegion().
List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();
Si la consulta forzare un refresco de esta regin de cach en particular, habra que invocar Query.setCacheMode(CacheMode.REFRESH). Esto es particularmente til para los casos en donde los datos subyacentes pudieren haber sido actualizados por un proceso separado (por ejemplo, no por Hibernate), y le permite a la aplicacin refrescar selectivamente un resultado en particular. Esto es ms eficiente que el desalojo de toda una regin de cachs via SessionFactory.evictQueries().
172 de 198
http://www.hibernar.org/documentacion_es/castellano.html
19.5.1. Taxonoma
Hibernate define tres tipos bsicos de coleccin: coleccin de valores asociaciones de-uno-a-muchos asociaciones de-muchos-a-muchos Esta clasificacin distingue entre las varias relaciones de tablas y claves forneas, pero en realidad no nos dice todo lo que necesitamos saber acerca del modelo relacional. Para comprender cabalmente la estructura relacional y las caractersticas de performance, debemos considerar tambin la estructura de la clave primaria que es usada por Hibernate para actualizar o borrar colecciones de filas. Esto a su vez sugiere la siguiente clasificacin: colecciones indexadas sets bags Todas las colecciones indexadas (maps, lists, arrays), tienen una clave primaria que consiste en las columnas de <key> e <index>. En este caso, las actualizaciones a la coleccin son extremadamente eficientes: la clave primaria puede ser indexada eficientemente y una fila en particular puede ser localizada eficientemente cuando Hibernate trate de actualizarla o borrarla. Los sets tienen una clave primaria que consiste en una <key> y columnas elemento. Esto puede ser menos eficiente para algunos tipos de elemento de coleccin, particularmente los elementos compuestos, o grandes campos de texto o binarios; la base de datos puede no ser capaz de indexar tan eficientemente una clave primaria compleja. Por otra parte, para asociaciones de-uno-a-muchos o de-muchos-a-muchos, particularmente cuando se usen identificadores sintticos, es muy probable que sea igual de eficiente. (Nota aparte: si se quiere que SchemaExport realmente cree por s solo la clave primaria de un <set>, debe declararse not-null="true" para todas las columnas). los mapeos <idbag> definen una clave sustituta, as que son muy eficientes de actualizar. De hecho, son el mejor caso. Las bags son el peor caso. Como una bag permite valores de elemento duplicados y no tiene columnas ndice, no se puede definir ninguna clave primaria. Hibernate no tiene forma de distinguir entre filas duplicadas. Hibernate resuelve este problema borrando completamente la coleccin (con un simple DELETE) y recrendola cada vez que algo cambia. Esto puede llegar a ser muy ineficiente. Note que para una asociacin de-uno-a-muchos, la "clave primaria" piede no ser la clave primiaria fsica de la tabla en la base de datos. Pero incluso en este caso, la clasificacin recin descripta es an til (an refleja cmo es que Hibernate "localiza" filas individuales de una coleccin).
19.5.2. Las lists, maps, idbags y sets son las colecciones ms eficientes de actualizar
Basndose en la discusin precedente, debera quedar claro que las colecciones indexadas y (normalmente) los sets permiten el desempeo ms eficiente en trminos de agregar, quitar, o actualizar elementos. Podra decirse las colecciones indexadas tienen una ventaja ms sobre los sets en las asociaciones de-muchos-a-muchos o colecciones de valores: a causa de la estructura de un Set, Hibernate nunca efecta un UPDATE de una fila cuando un elemento se "cambia". Los cambios en un Set siempre requieren INSERT(s) y DELETE(s) de filas individuales. Esta consideracin, de nuevo, no se aplica a las asociaciones de-uno-a-muchos. Tras observar que los arrays no pueden ser haraganes, concluiramos que las listas, mapas e idbags son los tipos (no inversos) de coleccin con mejor performance, seguidos de cerca por los sets. Los sets son el tipo ms comn de coleccin en Hibernate, porque la semntica de un set es la ms natural para un modelo relacional. Sin embargo, en un modelo de dominio Hibernate bien diseado, usualmente vemos que la mayra de las coleccioes son en realidad asociaciones de-uno-a-muchos con inverse="true". Para estas asociaciones, el UPDATE es manejado por el extremo 'de-muchos-a-uno' de la asociacin, as que estas disquisiciones sobre la performance al actualizar colecciones simplemente no son relevantes.
173 de 198
http://www.hibernar.org/documentacion_es/castellano.html
174 de 198
http://www.hibernar.org/documentacion_es/castellano.html
StatisticsService stats = new StatisticsService(); // implementacin del MBean stats.setSessionFactory(sessionFactory); // liga las estadsticas a la SessionFactory server.registerMBean(stats, on); // registra el Mbean em el servidor // servicio de registro de MBean para todas las SessionFactorys Hashtable tb = new Hashtable(); tb.put("type", "statistics"); tb.put("sessionFactory", "all"); ObjectName on = new ObjectName("hibernate", tb); // nombre del objeto MBean StatisticsService stats = new StatisticsService(); // implementacin del MBean server.registerMBean(stats, on); // Registra el MBean en el servidor
A hacer: esto no tiene sentido. En el primer caso, obtenemos y usamos el MBean dorectamente. En el segundo, debemos dar el nombre JNDI en el cual la fbrica de sesiones est contenida antes de usarla. Usar
hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")
Se puede activar/desactivar el monitoreo para una SessionFactory: en tiempo de configuracin, asgnesele a hibernate.generate_statistics el valor false en tiempo de ejecucin, sf.getStatistics().setStatisticsEnabled(true) ohibernateStatsBean.setStatisticsEnabled(true) Las estadsticas pueden ser reinicializadas en forma programtica, usando el mtodo clear(). Se puede mandar un sumario al log (a nivel info). usando el mtodo logSummary().
19.6.2. Mediciones
Hibernate provee un buen nmero de mediciones, desde muy bsicas, hasta informacin especializada que slo es relevante en ciertos escenarios. Todos los contadores relevantes estn descriptos en la API de Statistics, en tres categoras: Las mediciones relacionadas con el uso de la sesin en general, tales como el nmero de desiones abiertas, el nmero de conexiones JDBC obtenidas, etc. Mediciones relacionadas con las entidades, colecciones, consultas y cachs como un todo (es decir, mediciones globales). Mediciones detalladas relacionadas con una entidad, coleccin, consulta o regin de cach en particular. Por ejemplo, se puede chequear la proporcin de veces en que se da en el blanco, no se da en el blanco, o se inserta en el cach (en ingls: hit, miss and put ratio), para las entidades, las colecciones y las consultas, y el tiempo que, en promedio, necesita una consulta. Tenga en cuenta que el nmero de milisegundos es slo una aproximacin en Java. Hibernate est atado a la precisin de la JVM, y en algunas plataformas sta tiene solo un grado de exactitud de 10 segundos. Para acceder a las mediciones globales (es decir, no atadas a una entidad, cach, etc en particular) se usan simples mtodos "getter". Se puede acceder a las mediciones de una entidad, coleccin o regin de cach en particular a travs del nombre, y a travs de su representacin en HQL o en SQL para las consultas. Por favor refirase a los JavaDocs de las APIs de Statistics, EntityStatistics, CollectionStatistics, SecondLevelCacheStatistics, y QueryStatistics para ms informacin. El siguiente cdigo muestra un ejemplo simple:
Statistics stats = HibernateUtil.sessionFactory.getStatistics(); double queryCacheHitCount = stats.getQueryCacheHitCount(); double queryCacheMissCount = stats.getQueryCacheMissCount(); double queryCacheHitRatio = queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount); log.info("Query Hit ratio:" + queryCacheHitRatio); EntityStatistics entityStats = stats.getEntityStatistics( Cat.class.getName() ); long changes = entityStats.getInsertCount() + entityStats.getUpdateCount() + entityStats.getDeleteCount(); log.info(Cat.class.getName() + " changed " + changes + "times"
);
175 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Para trabajar en todas las entidades, colecciones, consultas y regiones de cach, se pued obtener la lista de nombres de entidades, colecciones, consultas y regiones con los siguientes mtodos: getQueries(), getEntityNames(), getCollectionRoleNames(), y getSecondLevelCacheRegionNames().
Algunas tags tambin aceptan un atributo not-null (para generar una constraint NOT NULL en las columnas de la tabla), y un atributo unique (para generar constraints de unicidad en las columnas de la tabla).
<many-to-one name="bar" column="barId" not-null="true"/>
176 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Se puede usar un atributo unique-key para agrupar columnas en una simple constraint de unicidad. Al presente, el valor especificado del atributo unique-key no es usado para nombrar la constraint en el DDL generado, solamente para agrupar a las columnas en el archivo de mapeo.
<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/> <property name="employeeId" unique-key="OrgEmployee"/>
Un atributo index especifica el nombre de un ndice a crear utilizando la columna o columnas mapeadas. Se puede agrupar mltiples columnas en en mismo ndice, simplemente especificando el mismo nombre de ndice.
<property name="lastName" index="CustName"/> <property name="firstName" index="CustName"/>
Un atributo foreign-key puede ser usado para sustituir el nombre generado de cualquier constraint de clave fornea.
<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>
Muvhos elementos de mapeo tambin aceptan un elemento <column> hijo. Esto es particularmente til al mapear tipos multicolumna:
<property name="name" type="my.customtypes.Name"/> <column name="last" not-null="true" index="bar_idx" length="30"/> <column name="first" not-null="true" index="bar_idx" length="20"/> <column name="initial"/> </property>
El atributo default permite especificar un valor por defecto pra la columna (debera asignrsele el mismo valor a la propiedad mapeada antes de grabar una nueva instancia de la clase mapeada).
<property name="credits" type="integer" insert="false"> <column name="credits" default="10"/> </property> <version name="version" type="integer" insert="false"> <column name="version" default="0"/> </property>
El atributo sql-type le permite al usuario sustituir el mapeo por defecto de un tipo de dato Hibernate a un tipo de dato SQL.
<property name="balance" type="float"> <column name="balance" sql-type="decimal(13,3)"/> </property>
Valores numricos numricos numrico largo de la columna precisin decimal de la columna escala decimal de la columna
Interpretacin
177 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Atributo
not-null unique index unique-key
Valores
true|false true|false index_name unique_key_name
Interpretacin especifica que la columna debera ser no anulable especifica que la columna debera tener una constraint de unicidad especifica el nombre de un ndice multicolumna especifica el nombre de una constraint de unicidad multicolumna especifica el nombre de una constraint de clave fornea generada para una asociacin, para un elemento de mapeo <one-to-one>, <many-to-one>, <key>, o <many-to-many>. Note que los extremos con inverse="true" sern ignorados por el SchemaExport. sustituye el tipo de columna por defecto (atributo del elemento <column> solamente) especifica un valor por defecto para la columna crea una constraint de chequeo, sea para la tabla o para la columna
foreign-key foreign_key_name
Esto resulta en comandos comment on table o comment on column en el DDL generado, cuando esto se soportada.
Descripcin no imprima el script en la salida estndar slo elimine (drop) las tablas slo cree las tablas n exporte a la base de datos escriba el script DDL en un archivo seleccione una NamingStrategy (estrategia de nombrado) lea la configuracin de Hibernate de una archivo XML lea las propiedades de la BD de un archivo formatee prolijamente el SQL generado en el script agrguele un delimitador de fin de lnea al script
178 de 198
http://www.hibernar.org/documentacion_es/castellano.html
20.1.3. Propiedades
Se puede especificar propiedades de base de datos: como propiedades de sistema con -D<propiedad> en hibernate.properties en un archivo nombrado, con --properties Las propiedades que se necesitan son: Tabla 20.3. Porpiedades de conexin de SchemaExport Nombre de la propiedad
hibernate.connection.driver_class hibernate.connection.url hibernate.connection.username hibernate.connection.password hibernate.dialect
Descripcin clase del driver jdbc URL jdbc usuario de la base de datos database user clave del usuario dialecto
Descripcin no imprima el script en la salida estndar no exporte el script a la base de datos seleccione una NamingStrategy
179 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Opcin
--properties=hibernate.properties --config=hibernate.cfg.xml
Descripcin lea las propiedades de base de datos de un archivo especifique un archivo .cfg.xml
20.1.6. Utilizar Ant para las actualizaciones incrementales del esquema de base de datos
Se puede invocar a SchemaUpdate desde el script de Ant:
<target name="schemaupdate"> <taskdef name="schemaupdate" classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask" classpathr <schemaupdate properties="hibernate.properties" quiet="no"> <fileset dir="src"> <include name="**/*.hbm.xml"/> </fileset> </schemaupdate> </target>
Descripcin selecciona una NamingStrategy lee las propiedades de base de datos de una archivo especifica un archivo .cfg.xml
180 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Una de las primeras cosas que los nuevos usuarios tratan de hacer con Hibernate, es modelar una relacin del tipo padre/hijo (en ingls, "parent" y "child" respectivamente). Hay dos abordajes diferentes para esto. Por varias razones, el abordaje ms conveniente, especialmente para usuarios nuevos, es modelar tanto Padre como Hijo como clases de entidad, con una asociacin de-uno-a-muchos (<one-to-many>) del Padre al Hijo. (El enfoque alternativo es declarar el Hijo como un elemento compuesto ( <composite-element>). Ahora bien, ocurre que la semntica por defecto de una asociacin de-uno-a-muchos (en Hibernate), es mucho ms parecida a la semntica usual de una relacin padre/hijo, que a la semntica del mapeo de un elemento compuesto. Explicaremos cmo usar una asociacin 'de-uno-a-muchos bidireccional con propagacin en cascada' para modelar una relacin padre/hijo eficiente y elegantemente. No es nada difcil!
Hibernate emitir los siguientes comandos SQL: un INSERT para crear el registro para c un UPDATE para crear el vnculo de p a c Esto es no solamente ineficiente, sino que viola cualquier constraint NOT NULL que pudiere haber en la columna parent_id column. Podemos areglar la violacin de la constraint de nulabilidad especificando not-null="true" en el mapeo de la coleccin:
<set name="children"> <key column="parent_id" not-null="true"/> <one-to-many class="Child"/> </set>
181 de 198
http://www.hibernar.org/documentacion_es/castellano.html
De todos modos, sta no es la solucin que se recomienda. La causa subyacente de este comportamiento es que el vnculo (la clave fornea parent_id) de p a c no se considera como parte del estado del objeto Child y por lo tanto no es creado durante el INSERT. As que la solucin es volver al vnculo que es parte del mapeo del objeto Child.
<many-to-one name="parent" column="parent_id" not-null="true"/>
(Tambin es necesario agragar la propiedad parent a la clase Child). Ahora que la entidad Child est manejando el estado del vnculo, le indicamos a la coleccin que no actualice el vnculo ella. Para esto se usa el atributo inverse.
<set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
Y ahora, slo se emite un comando INSERT! Para afinar las cosas un poco ms, podramos crear un mtodo addChild() en la clase Parent.
public void addChild(Child c) { c.setParent(this); children.add(c); }
En forma similar, no necesitamos iterar a travs de los hijos al grabar o borrar un Parent. Lo siguiente borra a p y a todos sus hijos de la base de datos:
Parent p = (Parent) session.load(Parent.class, pid); session.delete(p); session.flush();
182 de 198
http://www.hibernar.org/documentacion_es/castellano.html
no borrar a c de la base de datos; slo quitar el vnculo con p (y causar en este caso una violacin de constraint NOT NULL). Hay que borrar al (con delete()) explcitamente al Child.
Parent p = (Parent) session.load(Parent.class, pid); Child c = (Child) p.getChildren().iterator().next(); p.getChildren().remove(c); session.delete(c); session.flush();
Ahora, en este caso, un Child no puede realmente existir sin su padre. Entonces, si quitamos un Child de una coleccin, realmente queremos que sea borrado. Para ello debemos usar cascade="all-delete-orphan".
<set name="children" inverse="true" cascade="all-delete-orphan"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
Nota: an cuando el mapeo de la coleccin especifica inverse="true", ls propagaciones en cascada s se procesan iterando a travs de los elementos de la coleccin. As que si se necesita que una coleccin sea grabada, borrada o actualizada en cascada, se debe agregar el atributo "cascade" a la coleccin. No basta con invocar setParent().
Todo esto est muy bien en el caso de un identificador autogenerado, pero qu pasas con los identificadores asignados, o con los identificadores compuestos? Esto es ms difcil, dado que Hibernate no puede usar la propiedad identificadora para distinguir entre un objeto recientemente instanciado (con un identificador asignado por el usuario) y un objeto cargado en una sesin previa. En este caso, Hibernate usar o bien las propiedades version/timestamp, o bien consultar el cach de 2do nivel, o, en el peor de los casos, la base de datos, para verificar si la fila existe.
21.5. Conclusin
Esto es bastante para digerir de una vez, y puede parecer confuso la primera vez. Pero en la prctica todo se acomoda y funciona en una forma muy linda. La mayora de las aplicaciones Hibernate utilizan el patrn padre/hijo en muchos lugares. En el primer prrafo mencionbamos una alternativa. En el caso de los mapeos con <composite-element> no existe ninguno de estos problemas, que tienen exactamente la semntica de una relacin padre/hijo. Desafortunadamente, existen dos grandes limitaciones al trabajar con clases de elementos compuestos: un elemento compuesto no puede contener colecciones, y ellos mismos slo pueden ser hijos de una nica entidad padre.
183 de 198
http://www.hibernar.org/documentacion_es/castellano.html
184 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<bag name="items" inverse="true" order-by="DATE_TIME" cascade="all"> <key column="BLOG_ID"/> <one-to-many class="BlogItem"/> </bag> </class> </hibernate-mapping> <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="eg"> <class name="BlogItem" table="BLOG_ITEMS" dynamic-update="true"> <id name="id" column="BLOG_ITEM_ID"> <generator class="native"/> </id> <property name="title" column="TITLE" not-null="true"/> <property name="text" column="TEXT" not-null="true"/> <property name="datetime" column="DATE_TIME" not-null="true"/> <many-to-one name="blog" column="BLOG_ID" not-null="true"/> </class> </hibernate-mapping>
185 de 198
http://www.hibernar.org/documentacion_es/castellano.html
public class BlogMain { private SessionFactory _sessions; public void configure() throws HibernateException { _sessions = new Configuration() .addClass(Blog.class) .addClass(BlogItem.class) .buildSessionFactory(); } public void exportTables() throws HibernateException { Configuration cfg = new Configuration() .addClass(Blog.class) .addClass(BlogItem.class); new SchemaExport(cfg).create(true, true); } public Blog createBlog(String name) throws HibernateException { Blog blog = new Blog(); blog.setName(name); blog.setItems( new ArrayList() ); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.persist(blog); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return blog; } public BlogItem createBlogItem(Blog blog, String title, String text) throws HibernateException BlogItem item = new BlogItem(); item.setTitle(title); item.setText(text); item.setBlog(blog); item.setDatetime( Calendar.getInstance() ); blog.getItems().add(item); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.update(blog); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return item; } public BlogItem createBlogItem(Long blogid, String title, String text) throws HibernateExceptio BlogItem item = new BlogItem(); item.setTitle(title); item.setText(text); item.setDatetime( Calendar.getInstance() );
186 de 198
http://www.hibernar.org/documentacion_es/castellano.html
Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); Blog blog = (Blog) session.load(Blog.class, blogid); item.setBlog(blog); blog.getItems().add(item); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return item; } public void updateBlogItem(BlogItem item, String text) throws HibernateException { item.setText(text); Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.update(item); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } } public void updateBlogItem(Long itemid, String text) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); BlogItem item = (BlogItem) session.load(BlogItem.class, itemid); item.setText(text); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } } public List listAllBlogNamesAndItemCounts(int max) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; List result = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "select blog.id, blog.name, count(blogItem) " + "from Blog as blog " + "left outer join blog.items as blogItem " + "group by blog.name, blog.id " + "order by max(blogItem.datetime)" ); q.setMaxResults(max); result = q.list(); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; }
187 de 198
http://www.hibernar.org/documentacion_es/castellano.html
finally { session.close(); } return result; } public Blog getBlogAndAllItems(Long blogid) throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; Blog blog = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "from Blog as blog " + "left outer join fetch blog.items " + "where blog.id = :blogid" ); q.setParameter("blogid", blogid); blog = (Blog) q.uniqueResult(); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return blog; } public List listBlogsAndRecentItems() throws HibernateException { Session session = _sessions.openSession(); Transaction tx = null; List result = null; try { tx = session.beginTransaction(); Query q = session.createQuery( "from Blog as blog " + "inner join blog.items as blogItem " + "where blogItem.datetime > :minDate" ); Calendar cal = Calendar.getInstance(); cal.roll(Calendar.MONTH, false); q.setCalendar("minDate", cal); result = q.list(); tx.commit(); } catch (HibernateException he) { if (tx!=null) tx.rollback(); throw he; } finally { session.close(); } return result; } }
23.1. Empleador/Empleado
El modelo siguiente de la relacin entre empleador y empleado (Employer y Employee) usa una verdadera clase de entidad, Employment (empleo) para representar la asociacn. Esto se hace porque puede haber msde un perodo de empleo para las mismas dos partes involucradas. Para modelar valores monetarios y nombres de empleados se usan componentes.
188 de 198
http://www.hibernar.org/documentacion_es/castellano.html
189 de 198
http://www.hibernar.org/documentacion_es/castellano.html
id BIGINT not null, name VARCHAR(255), primary key (id) ) create table employment_periods ( id BIGINT not null, hourly_rate NUMERIC(12, 2), currency VARCHAR(12), employee_id BIGINT not null, employer_id BIGINT not null, end_date TIMESTAMP, start_date TIMESTAMP, primary key (id) ) create table employees ( id BIGINT not null, firstName VARCHAR(255), initial CHAR(1), lastName VARCHAR(255), taxfileNumber VARCHAR(255), primary key (id) ) alter table employment_periods add constraint employment_periodsFK0 foreign key (employer_id) refer alter table employment_periods add constraint employment_periodsFK1 foreign key (employee_id) refer create sequence employee_id_seq create sequence employment_id_seq create sequence employer_id_seq
23.2. Autor/Obra
Considrese el modelo siguiente de la relacin entre obra, autor y persona (Work, Author y Person respectivamente). Representamos la relacin entre Work y Author como un asociacin de-muchos-a-muchos. Elegimos representar la asociacin entre Author y Person como de-uno-a-uno. Otra posibilidad sera hacer que Author extendiere Person.
190 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<discriminator column="type" type="character"/> <property name="title"/> <set name="authors" table="author_work"> <key column name="work_id"/> <many-to-many class="Author" column name="author_id"/> </set> <subclass name="Book" discriminator-value="B"> <property name="text"/> </subclass> <subclass name="Song" discriminator-value="S"> <property name="tempo"/> <property name="genre"/> </subclass> </class> <class name="Author" table="authors"> <id name="id" column="id"> <!-- el Autor tiene que tener el mismo identificador que la Persona --> <generator class="assigned"/> </id> <property name="alias"/> <one-to-one name="person" constrained="true"/> <set name="works" table="author_work" inverse="true"> <key column="author_id"/> <many-to-many class="Work" column="work_id"/> </set> </class> <class name="Person" table="persons"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name"/> </class> </hibernate-mapping>
En este mapeo hay 4 tablas. works, authors y persons contienen las obras, autores y personas respectivamente. author_work es una tabla de asociacin que vincula a los autores con sus obras. Aqu est el esquema de tablas, tal cual es generado por SchemaExport.
create table works ( id BIGINT not null generated by default as identity, tempo FLOAT, genre VARCHAR(255), text INTEGER, title VARCHAR(255), type CHAR(1) not null, primary key (id) ) create table author_work ( author_id BIGINT not null, work_id BIGINT not null, primary key (work_id, author_id) ) create table authors ( id BIGINT not null generated by default as identity, alias VARCHAR(255), primary key (id) ) create table persons ( id BIGINT not null generated by default as identity, name VARCHAR(255), primary key (id) ) alter table authors add constraint authorsFK0 foreign key (id) references persons
191 de 198
http://www.hibernar.org/documentacion_es/castellano.html
alter table author_work add constraint author_workFK0 foreign key (author_id) references authors alter table author_work add constraint author_workFK1 foreign key (work_id) references works
23.3. Cliente/Orden/Producto
Ahora consideremos un modelo de las relaciones entre cliente, orden, tem (es decir, lnea de la orden) y producto (Customer, Order, LineItem y Product respectivamente). Hay una asociacin de-uno-a-muchos entre Customer y Order, pero cmo deberamos representar Order / LineItem / Product? Elijo mapear LineItem como una clase de asociacin que representa la asociacin de-muchos-a-muchos entre Order y Product. En Hibernate, esto se denomina un "elemento compuesto" (composite element).
El documento de mapeo:
<hibernate-mapping> <class name="Customer" table="customers"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="orders" inverse="true"> <key column="customer_id"/> <one-to-many class="Order"/> </set> </class> <class name="Order" table="orders"> <id name="id"> <generator class="native"/> </id> <property name="date"/> <many-to-one name="customer" column="customer_id"/> <list name="lineItems" table="line_items"> <key column="order_id"/> <list-index column="line_number"/> <composite-element class="LineItem"> <property name="quantity"/> <many-to-one name="product" column="product_id"/> </composite-element> </list> </class> <class name="Product" table="products"> <id name="id"> <generator class="native"/> </id> <property name="serialNumber"/> </class> </hibernate-mapping> customers, orders, line_items y products contienen los datos de clientes, rdenes, items de las rdenes y productos, respectivamente. line_items tambin acta como una tabla de asociacin que vincula rdenes con productos. create table customers ( id BIGINT not null generated by default as identity, name VARCHAR(255),
192 de 198
http://www.hibernar.org/documentacion_es/castellano.html
primary key (id) ) create table orders ( id BIGINT not null generated by default as identity, customer_id BIGINT, date TIMESTAMP, primary key (id) ) create table line_items ( line_number INTEGER not null, order_id BIGINT not null, product_id BIGINT, quantity INTEGER, primary key (order_id, line_number) ) create table products ( id BIGINT not null generated by default as identity, serialNumber VARCHAR(255), primary key (id) ) alter table orders add constraint ordersFK0 foreign key (customer_id) references customers alter table line_items add constraint line_itemsFK0 foreign key (product_id) references products alter table line_items add constraint line_itemsFK1 foreign key (order_id) references orders
193 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<key column="customerId"/> <index column="orderNumber"/> <one-to-many class="Order"/> </list> </class> <class name="Order" table="CustomerOrder" lazy="true"> <synchronize table="LineItem"/> <synchronize table="Product"/> <composite-id name="id" class="Order$Id"> <key-property name="customerId" length="10"/> <key-property name="orderNumber"/> </composite-id> <property name="orderDate" type="calendar_date" not-null="true"/> <property name="total"> <formula> ( select sum(li.quantity*p.price) from LineItem li, Product p where li.productId = p.productId and li.customerId = customerId and li.orderNumber = orderNumber ) </formula> </property> <many-to-one name="customer" column="customerId" insert="false" update="false" not-null="true"/> <bag name="lineItems" fetch="join" inverse="true" cascade="save-update"> <key> <column name="customerId"/> <column name="orderNumber"/> </key> <one-to-many class="LineItem"/> </bag> </class> <class name="LineItem"> <composite-id name="id" class="LineItem$Id"> <key-property name="customerId" length="10"/> <key-property name="orderNumber"/> <key-property name="productId" length="10"/> </composite-id> <property name="quantity"/> <many-to-one name="order" insert="false" update="false" not-null="true"> <column name="customerId"/> <column name="orderNumber"/> </many-to-one> <many-to-one name="product" insert="false" update="false" not-null="true" column="productId"/> </class> <class name="Product"> <synchronize table="LineItem"/> <id name="productId" length="10"> <generator class="assigned"/> </id> <property name="description" not-null="true" length="200"/> <property name="price" length="3"/> <property name="numberAvailable"/> <property name="numberOrdered"> <formula> ( select sum(li.quantity)
194 de 198
http://www.hibernar.org/documentacion_es/castellano.html
</class>
<discriminator type="character"> <formula> case when title is not null then 'E' when salesperson is not null then 'C' else 'P' end </formula> </discriminator> <property name="name" not-null="true" length="80"/> <property name="sex" not-null="true" update="false"/> <component name="address"> <property name="address"/> <property name="zip"/> <property name="country"/> </component>
195 de 198
http://www.hibernar.org/documentacion_es/castellano.html
<subclass name="Employee" discriminator-value="E"> <property name="title" length="20"/> <property name="salary"/> <many-to-one name="manager"/> </subclass>
<id name="id"> <generator class="hilo"/> </id> <property name="name" length="100"/> <one-to-one name="address" property-ref="person" cascade="all" fetch="join"/> <set name="accounts" inverse="true"> <key column="userId" property-ref="userId"/> <one-to-many class="Account"/> </set> <property name="userId" length="8"/> </class> <class name="Address"> <id name="id"> <generator class="hilo"/> </id> <property name="address" length="300"/> <property name="zip" length="5"/> <property name="country" length="25"/> <many-to-one name="person" unique="true" not-null="true"/> </class> <class name="Account"> <id name="accountId" length="32"> <generator class="uuid"/> </id> <many-to-one name="user" column="userId" property-ref="userId"/> <property name="type" not-null="true"/> </class>
196 de 198
http://www.hibernar.org/documentacion_es/castellano.html
significado de negcocios). Identifique claves naturales. Identifique claves naturales para todas las entidades, y mapelas usando <natural-id>. Implemente equals() y hashCode() para comparar las propiedades que componen la clave natural. Ubique cada mapeo de clase en su propio archivo. No use un nico documento de mapeo monoltico. Mapee com.eg.Foo en el archivo com/eg/Foo.hbm.xml. Esto tiene sentido, particularmente en un entorno de programacin en equipo. Cargue los archivos de mapeo como recursos. Despliegue los mapeos junto con las clases que mapean. Considere externalizar las cadenas SQL de las consultas. sta es una buena prctica si sus consultas utilizan funciones SQL que no sean estndar de ANSI. Externalizarlas volver la aplicacin ms porttil. Use variables de vnculo. Como en JDBC, siempre reemplace valores no constantes con "?". Nunca use manipulacin de cadenas para unir un valor no-constante a una consulta! Mejor an, considere usar parmetros nombrados en las consultas. No maneje sus propias conexiones JDBC. Hibernate le permite a la aplicacin manejar sus conexiones JDBC, pero esto debe considerarse como un ltmo recurso. Si no puede usar los proveedores de conexiones que ya vienen incluidos, considere proveer su propia implementacin de org.hibernate.connection.ConnectionProvider. Considere usar un tipo a medida. Suponga que tiene un tipo Java, digamos de una biblioteca, que necesita ser persistido, pero no provee los mtodos de acceso necesarios para mapearlo como un componente. Usted debera considerar implementar org.hibernate.UserType. Esta estrategia libera al cdigo de la aplicacin de implementar transformaciones desde o hacia un tipo Hibernate. En cuellos de botella, use JDBC manual. En reas de performance crticas de un sistema, algunos tipos de operacin podran beneficiarse al usar JDBC directamente. Pero por favor, espere hasta estar seguro de que algo constituye un cuello de botella. Y no asuma que JDBC es necesariamente ms rpida. Si necesita utilizar JDBC directamente, puede valer la pena abrir una sesin y usar esa conexin JDBC. De este modo, usted an puede aprovechar la estrategia de transacciones y el proveedor de conexiones subyacente. Comprenda el "flush" de sesin De tanto en tanto, la sesin sincroniza su estado persistente con la base de datos. La performance se resentir si este proceso ocurre demasiado seguido. A veces se puede disminuir la cantidad de "flush" innecesarios inhabilitando el "flushing" automtico, o incluso cambiando el orden de las consultas u otras operaciones dentro de una transaccin en particular. En una arquitectura de tres capas, considere usar objetos desprendidos. Cuando se usa una arquitectura de servlet/session bean, se pueden pasar objetos persistentes cargados en el session bean desde y hacia la capa de servlets/JSP. Use una nueva sesin para servir a cada solicitud (request). Use Session.merge() o Session.saveOrUpdate() para sincronizar objetos con la base de datos. En una arquitectura de dos capas, considere usar contextos largos de persistencia. Las transacciones de DB tienen que ser lo ms cortas posible, para que la aplicacin sea escalable. Sin embargo, a veces es necesario implementar transacciones "de aplicacin" de largo aliento, una unidad de trabajo nica desde el punto de vista del usuario. Una "transaccin de aplicacin" puede extenderse por varios ciclos solicitud/respuesta del cliente. Es comn usar objetos desprendidos para implementar transacciones de aplicacin. Una alternativa,
197 de 198
http://www.hibernar.org/documentacion_es/castellano.html
extremadamente apropiada en arquitecturas de dos capas, es mantener un nico contacto de persistencia abierto (una sesin) para todo el ciclo de vida de la "transaccin de aplicacin", y simplemente desconectarse de la conexin JDBC al final de cada slicitud, reconectndose al comenzar la solicitud siguiente. Nunca comparta una misma sesin a travs de ms de una "transaccin de aplicacin" o estar trabajando con datos rancios. No trate las excepciones como si fueran recuperables. Esto una prctica necesaria ms que una "prctica recomendada". Cuando ocurra una excepcin, invoque un rollback de la transaccin, y cierre la sesin. Si no lo hace, Hibernate no puede garantizar que el estado en memoria represente correctamente el estado persistente. Como corolario de esto, no use Session.load() para determinar si una instancia con un identificador dado existe en la base de datos. En lugar de eso, use Session.get() o una consulta. Prefiera la captura haragana (lazy fetching) para las asociaciones. Use la captura ansiosa (eager fetching) con mesura. Use proxies y colecciones haraganas para la mayora de las asociaciones a clases que no es probable que sean retenidas en el cach de 2do nivel. Para asociaciones a clases cacheadas, en donde exista una probabilidad extremadamante alta de ubicarla en el cach, inhabilite explcitamente la captura haragana usando lazy="false". Cuando, para un caso de uso en particular, sea apropiado usar una captura por join, use una consulta con left join fetch. use el patrn open session in view, o una fase de ensamble (assembly phase) disciplinada para evitar problemas con datos no capturados. Hibernate libera al programador de la tediosa tarea de escribir objetos de transferencia de datos (DTO por sus siglas en ingls). En una arquitectura EJB tradicional, los DTOs tienen un doble propsito: uno, sortear el problema de que los entity beans no son serializables; el otro, que implcitamente definen una fase de ensamble en donde todos los datos usados por la interfaz de usuario o "view" tiene que ser capturados y puestos en orden en DTOs, antes de que el control le sea devuelto a la capa de presentacin. Hibernate elimina el primer propsito, pero an se necesita una fase de ensamble (imagnese a los mtodos de negocio como si tuvieran un estricto contrato con la capa de presentacin acerca de qu datos hay disponibles en los objetos desprendidos), a menos que se est dispuesto a mantener la sesin abierta a travs de la capa de presentacin. Esto no es una limitacin de Hibernate, sino un requerimiento fundamental para un acceso seguro a datos transaccionales. Considere abstraer su lgica de negocios, separndola de Hibernate. Oculte el cdigo (Hibernate) de acceso a datos detrs de una interfaz. Combine los partrones DAO y Sesin Thread Local. Se puede hacer incluso que algunas clases sean persistidas por JDBC escrita a mano, y asociadas a Hibernate por medio de un UserType. (Este consejo se aplica slo a aplicaciones "lo suficientemente largas", no es adecuado para una aplicacin con 5 tablas!) No use mapeos de asociacin exticos. Los casos de uso bien definidos, que involucren verdaderas asociaciones de-muchos-a-muchos, son raros. En general se necesita que la "tabla de vnculo" almacene informacin adicional. En ese caso, es mucho mejor usar dos asociaciones de-uno-a-muchos a una clase de vnculo intermedia. De hecho, creemos que la mayora de las asociaciones son de-uno-a-muchos y de-uno-a-uno. Hay que tener cuidado al usar cualquier otro estilo de asociacin, y preguntarse si es verdaderamente necesario. Prefiera las asociaciones bidireccionales. Las asociaciones unidireccionales son ms difciles de consultar. En aplicaciones grandes, casi todas las asociaciones deben ser navegables por consultas en ambas direcciones.
198 de 198