0% encontró este documento útil (0 votos)
25 vistas71 páginas

Java Hibernate

Este documento describe los conceptos fundamentales de la persistencia de objetos, incluyendo las diferencias entre los modelos relacional y orientado a objetos, y cómo los ORM (Object-Relational Mapping) ayudan a superar las brechas entre ambos modelos al mapear automáticamente objetos a tablas de bases de datos. También explica conceptos como herencia, identidad, asociaciones y navegación, y cómo los ORM resuelven problemas comunes en estas áreas.

Cargado por

Crispo Mar
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
25 vistas71 páginas

Java Hibernate

Este documento describe los conceptos fundamentales de la persistencia de objetos, incluyendo las diferencias entre los modelos relacional y orientado a objetos, y cómo los ORM (Object-Relational Mapping) ayudan a superar las brechas entre ambos modelos al mapear automáticamente objetos a tablas de bases de datos. También explica conceptos como herencia, identidad, asociaciones y navegación, y cómo los ORM resuelven problemas comunes en estas áreas.

Cargado por

Crispo Mar
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 71

CLASE 1

PERSISTENCIA DE OBJETOS

Qué es la persistencia

Definición

La persistencia es uno de los aspectos fundamentales en cualquier aplicación que


trabaja con datos. Representa el acto de persistir los datos en el tiempo.

Se utiliza normalmente para almacenar datos en una base de datos. En aplicaciones


OO, persistencia significa persistir el estado del objeto en el tiempo.

Bases de datos relacionales

Las base de datos relacionales son la forma por excelencia de almacenar datos. Son
extremadamente flexibles y robustas para la administración de datos. Los DBMS
(DataBase Management Systems) poseen interfaces basadas en SQL para interactuar
con los datos.

SQL tiene como categorías a DDL y DML:

 DDL significa Data Definition Language, y se utiliza para la creación de esquemas y


tablas. Los comandos más conocidos son CREATE, ALTER y DROP.
 DML significa Data Manipulation Language, y se utiliza para la manipulación de
datos. Los comandos más conocidos son SELECT, INSERT, UPDATE, DELETE.

Archivos planos

Los archivos planos son otra forma de persistencia. Si bien es posible guardar casi
cualquier informacion, se hace relativamente difícil poder organizar y consultar de
manera eficiente la información deseada.

En caso de objetos, se podrían persistir en archivos planos a través de serialización, y


volver a utilizarlos a través de la deserealización.

Modelo Relacional vs. Modelo OO

Problemática

La problemática que se plantea es que los RDBMS (Relational DBMS) están basados
en el modelo relacional, pero por su lado las aplicaciones orientadas a objetos están
basadas en el paradigma de objetos.
Ambos modelos tienen sus propias características, y como consecuencia se genera la
problemática de tratar datos de una tabla como un objeto y viceversa, es decir llevar
datos de un objeto a una tabla.

Se genera lo que se conoce como un “gap” entre ambos modelos, dificultando la


integración y coordinación de ambos.

Una tabla, una clase

La forma más sencilla de relacionar ambos modelos es tratar a cada clase en el


modelo de objetos, como una tabla en el modelo relacional.

Se puede decir que “…una tabla es a una clase, lo que un objeto es a un registro…”.

Por ejemplo, la relación entre un cliente y sus facturas será la siguiente en una base
de datos:

La definición de las tablas quedará establecida por:

Copiar a Clipboard

CREATE TABLE Cliente {

cli_id INT NOT NULL PRIMARY KEY,

cli_nombre VARCHAR(255) NOT NULL

CREATE TABLE CuentaBancaria {

cta_id INT NOT NULL PRIMARY KEY,

cta_cli_id INT NOT NULL PRIMARY KEY,

cta_saldo FLOAT NOT NULL

Por otro lado, siguiendo el mismo ejemplo, la relación entre un cliente y una cuenta
bancaria será la siguiente en un diagrama de clases:
La definición de las clases quedará establecida por:

Copiar a Clipboard

public class Cliente {

int id;

String nombre;

List cuentasBancarias;

public class CuentaBancaria {

int id;

Cliente cliente;

float saldo;

En este caso no hay problemas, es la forma más sencilla de trabajar.

El problema de la herencia

Uno de los pilares del paradigma OO es la herencia. La herencia representa a la


relación “es un”. Es muy común trabajar con diagramas de clases que poseen una
gran jerarquía de clases a través de la herencia.
El modelo relacional está basado en la relación “tiene un”, pero no cuenta con la
relación “es un”. La problemática se basa en que dentro del modelo relacional, no es
posible armar una relación de herencia.

El problema de la identidad

Para realizar chequeos de identidad en registros, se utiliza la PK (Primary Key) como


método de comparación.

Para realizar chequeos de identidad en objetos, se utilizan los métodos equals() y


compareTo() según corresponda, como también el operador == dependiendo del
caso.

Suponiendo la siguiente definición de la tabla usuarios:

Copiar a Clipboard

CREATE TABLE usuarios {

usu_nombre VARCHAR(255) NOT NULL PRIMARY KEY,

usu_clave VARCHAR(255) NOT NULL

Suponiendo la siguiente definición de la clase Usuario:

Copiar a Clipboard

public class Usuario {

String nombre;
String clave;

La problemática surge cuando hay que realizar una actualización en cascada de la PK


desde el modelo de objetos – el campo nombre - ya que el impacto es grande y
genera procesamiento adicional que podría ser evitable.

Para evitar este tipo de problemática, la recomendación es utilizar una PK que nada
tenga que ver con los datos, posiblemente un campo auto-numérico, y construir un
atributo adicional ID en la clase correspondiente.

La nueva definición de la tabla usuarios podría quedar de la siguiente manera:

Copiar a Clipboard

CREATE TABLE usuarios {

usu_id INT NOT NULL PRIMARY KEY,

usu_nombre VARCHAR(255) NOT NULL UNIQUE,

usu_clave VARCHAR(255) NOT NULL

La nueva definición de la clase Usuario podría quedar de la siguiente manera:

Copiar a Clipboard

public class Usuario {

int id;

String nombre;

String clave;

El problema de las asociaciones


En el paradigma de objetos, las asociaciones representan la forma de relacionar
objetos.

Un objeto podría tener a otro objeto como asociado, por ejemplo un Auto tiene un
Stereo, entonces es posible decir que la clase Auto tiene como atributo un objeto del
tipo Stereo (obviamente, Stereo tiene sus propios atributos).

En el modelo relacional, dichas asociaciones se presentan a través de las claves


foráneas.

Las asociaciones tienen dirección, es decir que cada clase que forma parte de la
relación, tiene (o no) una referencia a la otra clase.

En una relación unidireccional, una de las dos clases tiene como referencia a la otra.
Por ejemplo, el Auto tiene como referencia al Stereo, o podría ser al revés también,
el Stereo tiene como referencia al Auto.

Copiar a Clipboard

public class Auto

private Stereo stereo;

public class Stereo

// Atributos

En una relación bidireccional, ambas clases se tienen como referencia. Por ejemplo,
el Auto tiene como referencia al Stereo y el Stereo tiene como referencia al Auto.

Copiar a Clipboard

public class Auto

private Stereo stereo;


}

public class Stereo

private Auto auto;

Las claves foráneas no son en su concepción bidireccionales, lo que generaría


inicialmente un desacople entre el modelo relacional y el modelo de objetos.

Un objeto podría tener a un conjunto (una colección) de objetos asociados, por


ejemplo un Aula tiene Alumnos. La clase Aula tiene como atributo un objeto del tipo
Set que contiene objetos del tipo Alumno (obviamente, cada Alumno tiene sus
propios atributos).

Copiar a Clipboard

public class Aula

private Set<Alumno> alumnos;

Es normal contar con relaciones del tipo muchos-a-muchos, por ejemplo un Aula
posee muchos alumnos, pero un Alumno puede tener asignada más de un Aula.

Copiar a Clipboard

public class Aula

private Set<Alumno> alumnos;

public class Alumno


{

private Set<Aula> aulas;

En el modelo de objetos, existen únicamente dos clases, pero en el modelo relacional


surge una nueva tabla (llamada tabla de enlace o link table) para manejar la relación
muchos-a-muchos.

El problema de la navegación

La relación entre clases está dada a través de una nueva clase, o colecciones. Por
ejemplo, la clase Universidad tiene Facultades, la Facultad tiene Alumnos, el
Alumno tiene Asignaturas, y así sucesivamente.

El problema está en que al realizar la consulta hay que determinar hasta qué nivel de
información (es decir hasta qué objetos) traer como datos.

El problema de la navegación es uno de los problemas que más impacta sobre la


performance de la aplicación.

Qué es ORM

Definición

ORM significa Object / Relational Mapping. Es el middleware que maneja la


persistencia en la capa de acceso a datos.

Tiene como objetivo manejar la persistencia de objetos de una aplicación dentro de


una base de datos, en forma automatizada y transparente. Utiliza metadatos para
describir la relación entre el modelo de clases y el modelo de tablas.

Maneja los casos más comunes, que son los que aparecen en mayor cantidad. Los
casos especiales deben ser manejados por el usuario, aunque éstos deberían ser los
mínimos.

El uso de un ORM suele disminuir el tiempo de construcción de una aplicación en un


30%. Se estima que el trabajo de desarrollo de la capa de acceso a datos disminuye
en un 85%.

Organización

Una solución del tipo ORM tiene como partes a:


1. Una API para realizar los ABMC en objetos de clases persistentes

2. Un lenguaje que permite realizar consultas referenciando a clases y atributos.

3. Una forma de especificar los mapeos entre meta-datos

Ventajas

Las ventajas que provee la utilización de un ORM son las siguientes:

 Independencia de la base de datos. Provee una abstracción de la base de datos


utilizada, lo cual permite - en caso de no utilizar SQL propietario – una fácil
migración.
 Productividad. Reduce significativamente el tiempo de desarrollo.
 Fácil mantenimiento. Agrega simplicidad al código, lo que hace que el
mantenimiento sea simplificado.
 Menor trabajo. Disminuye de forma importante las cantidad de líneas de código de la
aplicación, ya que la mayoría del código ya está pre-escrito.

Tecnologías ORM

User-defined DAOs

DAO significa Data Access Object. Es un patrón de diseño utilizado para realizar
operaciones contra una base de datos, pero abstrayendo de la lógica de la misma. La
lógica de acceso a datos queda encapsulada dentro de una DAO con lo cual no es
visible para usuarios que utilizan el DAO.

El User-defined DAO es un DAO definido por el usuario. El desarrollador puede


armar su propio modelo de acceso a datos con la construcción de diversos DAOs,
formando así la DAL (Data Access Layer, o capa de acceso a datos).

Es una de las opciones más utilizadas para la persistencia de datos. En Java


puntualmente, se trabaja con SQL y JDBC directamente. No se necesita ningún
contenedor especial para utilizarlos. Son clases planas con funcionalidad.

Tiene como desventaja que se paga el precio de “reinventar la rueda”, debido a que
hay que realizar el 100% de la codificación.

EJB Entity Beans

Los EJB Entity Beans forman parte de una propuesta de Sun Microsystems
denominada Enterprise Java Beans. Si bien automatizan ciertos aspectos del acceso a
datos, resultan sumamente complejos para aplicaciones de pequeña y mediana
envergadura.
Necesitan estar desplegados dentro de una EJB Container, y como consecuencia el
container se vuelve una dependencia.

Existen dos categorías:

 CMP (Container Managed Persistence)


 BMP (Bean Managed Persistence)

En los entity beans BMP, el desarrollador debe realizar la mayor parte de la


programación de acceso a datos, cumpliendo con ciertas interfaces.

En los entity beans CMP, el contenedor se encarga de realizar en forma automática


las operaciones básicas de acceso de datos.

No es de las opciones más utilizadas porque requiere un EJB Container para poder
utilizarlos, lo cual agrega significativo complejidad que en la mayoría de los casos
resulta innecesaria.

Jpa

Sus siglas significan Java Persistence API, es una API de persistencia desarrollada
para la plataforma JAVA EE.

Todos los componentes de La API se encuentran definidos en el


paquete javax.persistence.

JPA es una especificación, lo que significa que por sí sola no se puede implementar.
Podemos utilizar annotations de JPA en nuestras clases, sin embargo sin una
implementación nada sucederá. Hibernate es una implementación de JPA, cumple
las especificaciones de JPA y además aporta sus propias annotations y
funcionalidades.

Cuando se utilizan las annotations de JPA con Hibernate en realidad se está


utilizando la implementación de Hibernate para las annotations de JPA. El beneficio
es que podemos cambiar el ORM que estamos utilizando por cualquier otro que
implemente JPA sin tener que modificar nuestro código.

Algunas implementaciones de JPA son:

 Hibernate
 ObjectDB
 TopLink
 CocoBase
 EclipseLink
 OpenJPA
 Kodo
 DataNucleus, antes conocido como JPOX
 Amber

Hibernate

Es un framework – construido en JAVA - que se encarga de manejar la persistencia


de datos. Es la implementación de un ORM.

Es un proyecto de código abierto no comercial (inicialmente), bajo los aspectos de la


licencia pública GNU. En el año 2003 se une con JBoss.org y consigue su lado
comercial: venta de soporte y capacitación.

Los fuentes/binarios están disponibles en http://www.hibernate.org

Tiene como objetivo ser una solución completa a la problemática de persistencia de


datos con tecnología Java, dejando al desarrollador concentrarse fundamentalmente
en los aspectos de negocio. Aumenta fuertemente la productividad y facilita la
administración de la capa de acceso a datos.

Uno de los aspectos importantes de Hibernate es que está construido con clases java
básicas, es decir que puedo agregar el framework a un proyecto propio. No se
requiere ningún tipo de container (por ejemplo, un application server).
La necesidad de una DAL (Data Access Layer)

Arquitectura multicapa (n-tier Architecture)

Es una forma de organizar un sistema. Cada capa está compuesta - generalmente -


por un conjunto de clases que cooperan con un objetivo en común. Cada capa le
brinda servicios a la capa superior directa, no se realizan llamados entre capas “que
no se ven”.

Una de las arquitecturas más utilizadas es la arquitectura 3 capas:

 PL (Presentation Layer)
 BL (Business Layer o Business Logic)
 DAL (Data Access Layer)

Se la denomina 3TA à 3-Tier Architecture, y provee una separación lógica (no


necesariamente física) entre las 3 capas.
La PL, o Capa de Presentación, se encarga de administrar la lógica correspondiente a
mostrar la información en pantalla. Generalmente viene acompañada del uso del
patrón de diseño M-V-C.

La BL, o Capa de Negocios, se encarga de administrar la lógica de negocios, aquí es


donde reside la implementación de las reglas de negocio (Business Rules).

La DAL, o Capa de Acceso a Datos, se encarga de manejar cualquier comunicación


con la base de datos, ya sea para obtener información como para escribir.

Como ejemplo y basándonos en tecnología Java, la PL podría estar construida con


JSP y Struts, la BL con EJB de tipo session, y la DAL con Hibernate.

Qué es DAL

DAL significa Data Access Layer, y representa la capa de Acceso a Datos. Se


encarga de cualquier comunicación con la base de datos. Maneja todo tipo de
operaciones: operaciones DML tales como los AMBC, como también la posibilidad
de manejar DDL.

Generalmente, es un grupo de clases encargadas de realizar tanto la obtención de


datos como su persistencia. Le brinda servicios a la capa de negocios.

Qué es POJO

Un POJO es un Plain Old Java Object, aunque también a veces se lo denomina Plain
Ordinary Java Object. Es una clase que se encarga de manejar la persistencia.

Persigue las mismas características que los JavaBeans, es decir:

 Por lo general, es serializable, con lo cual implementa la interfaz (vacía) Serializable


 Posee atributos privados y métodos públicos de acceso
 Posee un constructor vacío, lo cual es obligatorio para trabajar con Hibernate

A continuación se presenta un ejemplo de un POJO:

Copiar a Clipboard

public class Usuario implements Serializable {

private String nombre;

private String clave;


public Usuario(){

// Setters y getters

...

...

Ventajas de una arquitectura Multi-Capa

Las ventajas de una arquitectura multi-capa son las siguientes:

 Provee una separación lógica entre las grandes tareas del sistema
 Permite que cada capa se concentre en una única tarea
 Aumenta la organización
 Aumenta la escalabilidad
 Facilita el mantenimiento

CONFIGURACIÓN DE HIBERNATE

Introducción

Para poder utilizar Hibernate resulta necesario agregar archivos .jar al proyecto

Jars necesarios

Archivos binarios

Para comenzar con la utilización de Hibernate, será necesario bajar el framework del
sitio de Hibernate:

http://www.hibernate.org

Una vez obtenido el archivo .zip con el framework, descompactarlo. Los archivos
principales/necesarios para su funcionamiento son los que están dentro de la
carpeta /lib/required/

hibernate.jar
Es el archivo principal de Hibernate. Contiene todas la clases fundamentales que
forman parte de Hibernate. Debe ser agregado a nuestro proyecto en el CLASSPATH
correspondiente. El nombre puede variar según la versión de Hibernate que se está
utilizando, desde las últimas versiones se llama hibernate-core (nro. de
versión).Final.jar, también se encuentra dentro de la carpeta /lib/required.

Otros .jar necesarios

Hibernate utiliza otros archivos jars para llevar a cabo su correcto funcionamiento.
Estos jars se encuentran también en la carpeta /lib/required/ del archivo zip.

Todos los archivos correspondientes a esta carpeta deberán ser agregados a nuestro
proyecto también en el CLASSPATH correspondiente.

JDBC Driver .jar

Otro de los aspectos importantes es el driver de conexión con la base de datos. El


driver es dependiente del DBMS a utilizar.

Tiene la forma de un archivo jar, y se puede obtener de

http://developers.sun.com/product/jdbc/drivers

Dicho archivo deberá ser agregado a nuestro proyecto en el CLASSPATH


correspondiente.

Links

El sitio de Hibernate es el punto de partida para obtener los archivos binarios, y para
profundizar el conocimiento:

http://www.hibernate.org/

Documentación

Hibernate posee su propia documentación. El javadoc de la API correspondiente se


encuentra en el sitio:

https://docs.jboss.org/hibernate/orm/4.3/javadocs/

INTRODUCCIÓN A UN PROYECTO CON HIBERNATE

Configuración

Jerarquía de directorios
Directorio /jar

Contiene los jars necesarios de hibernate, como el hibernate3.jar y todas sus


dependencias, como también los jars que necesita el proyecto para su correcto
funcionamiento. Los archivos contenidos en este directorio deben formar parte en la
variable de entorno CLASSPATH.

Paquete/directorio entities

Contiene las clases que se utilizan para representar a las tablas, junto con los archivos
de extensión xml que poseen el mapeo entre una clase y una tabla.

La conexión con la base de datos

La lógica de conexión con un DBMS en particular está contenida en un Driver, el


cual está contenido en un archivo .jar que deberá ser agregado también al proyecto,
dentro del directorio /jar.

Propiedades de Hibernate

Introducción

El archivo hibernate.properties es el archivo utilizado por Hibernate para configurar


su funcionamiento. Contiene líneas de texto con pares nombre-valor, por ejemplo
user=admin.

Existe una alternativa que es usar un archivo xml, formado por tags predefinidos, que
reemplaza al uso de archivos .properties.

hibernate.connection.driver_class

La propiedad hibernate.connection.driver_class representa el driver a utilizar.


Corresponde al nombre de la clase que implementa el driver JDBC, incluido en el jar
correspondiente. Dicho .jar, como ha sido especificado anteriormente, deberá formar
parte del CLASSPATH de la aplicación.

El driver depende directamente del DBMS. En el caso de MySQL, podría ser:

hibernate.connection.driver_class = com.mysql.jdbc.Driver

hibernate.connection.url

La propiedad hibernate.connection.url representa la url de conexión a utilizar.


Especifica el host, el puerto y la base de datos a utilizar. La url de conexión depende
directamente del DBMS.
En el caso de MySQL, teniendo en cuenta el servidor instalado en la máquina actual,
y un esquema de base de datos llamado hibernate, podría ser:

hibernate.connection.url = jdbc:mysql://localhost/hibernate

hibernate.connection.username

La propiedad hibernate.connection.username representa el usuario en la conexión a


utilizar. En el caso de MySQL, teniendo un usuario “admin”, podría ser:

hibernate.connection.username = usuario

hibernate.connection.password

La propiedad hibernate.connection.password representa la contraseña en la conexión


a utilizar. En el caso de MySQL, teniendo una contraseña “123456”, podría ser:

hibernate.connection.password = 123456

hibernate.dialect

La propiedad hibernate.dialect representa el dialecto (el lenguaje) a utilizar. Es


necesario determinarlo ya que la implementación de SQL es distinta en cada uno de
los DMBS propietarios. El dialecto depende directamente del DBMS.

En el caso de MySQL, podría ser:

hibernate.dialect = org.hibernate.dialect.MySQLDialect

Los dialectos disponibles

Existen dialectos definidos para cada uno de los DBMS. Los más conocidos se
detallan en la tabla siguiente

DBMS Dialecto
DB2 org.hibernate.dialect.DB2Dialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
Informix org.hibernate.dialect.InformixDialect
Pointbase org.hibernate.dialect.PointbaseDialect
PostgreSQL .hibernate.dialect.PostgreSQLDialect
Microsoft SQL
org.hibernate.dialect.SQLServerDialect
Server
MySQL org.hibernate.dialect.MySQLDialect
Oracle org.hibernate.dialect.OracleDialect
Progress org.hibernate.dialect.ProgressDialect
SAP DB org.hibernate.dialect.SAPDBDialect
Sybase org.hibernate.dialect.SybaseDialect

Ejemplo el archivo hibernate.properties

A continuación se presenta un ejemplo del archivo hibernate.properties,configurado


para trabajar con MySQL:

Copiar a Clipboard

hibernate.connection.driver_class=com.mysql.jdbc.Driver

hibernate.connection.url=jdbc:mysql://localhost/hibernate

hibernate.connection.username=root

hibernate.connection.password=root

hibernate.dialect=org.hibernate.dialect.MySQLDialect

Configuración del RDBMS

MySQL como RDBMS

Para obtener el MySQL Database Server, es posible bajarlo del siguiente link:

http://dev.mysql.com/downloads/mysql/4.1.html

Es conveniente trabajar con un cliente gráfico para realizar consultas, el propuesto


por MySQL es el MySQL Workbench, que se obtiene también del sitio de MySQL:

http://dev.mysql.com/downloads/workbench/

Creación de Base de datos y tablas

Para poder trabajar con Hibernate, se deberá crear el esquema de base de datos y las
tablas correspondientes. A continuación se presenta el código DDL para la creación
de una base de datos:

Copiar a Clipboard

CREATE DATABASE hibernate;

A continuación se presenta el código DDL de la creación de la tabla autos:

Copiar a Clipboard
CREATE TABLE autos

au_id int(10) unsigned NOT NULL auto_increment,

au_marca varchar(255) NOT NULL default '',

au_modelo varchar(255) NOT NULL default '',

PRIMARY KEY (au_id)

);

Creación de un POJO

Qué es un POJO

Cada tabla en el modelo relacional, tendrá una clase correspondiente en el modelo de


objetos. Las clases que se deseen persistir, deberán tener un tabla correspondiente en
la base de datos. Los atributos que se deseen persistir, deberán tener un campo
correspondiente en la tabla.

Para realizar este “mapeo” entre clases y tablas, es necesario que las clases sean
POJOs, es decir (Plain Old Java Object). Se denomina POJO a una clase que
contiene las características presentadas a continuación.

Características de un POJO

Para que la clase sea un POJO deberá cumplir con las características:

 Ser clases serializables, es decir implementar la interfaz Serializable


 Tener un constructor vacío
 Respetar el encapsulamiento, es decir atributos privados y métodos de acceso
públicos

Ejemplo de un POJO

A continuación se presenta la clase persistente Auto perteneciente al paquete entities:

Copiar a Clipboard

public class Auto implements Serializable {

private Long id;


private String marca;

private String modelo;

// Constructor vacío

public Auto(){}

// Constructor mas completo

public Auto(String unaMarca, String unModelo){

marca = unaMarca;

modelo = unModelo;

// Setters y getters

...

...

Hibernate Annotations

Qué son

Existen dos formas para realizar la transformación de información de un POJO a la


base de datos y viceversa. Una es mediante archivos XML y otra es con Annotations,
cada una tiene sus pros y sus contras, sin embargo las Annotations son la forma más
nueva de realizar el mapeo de POJOs a la base de datos, en este curso utilizaremos la
metodología de Annotations para la realización de todos los ejercicios.

Cuando se utiliza Hibernate Annotations toda la metadata está embebida en la misma


clase Java que contiene al POJO, junto con el código Java, esto ayuda al
desarrollador a relacionar la estructura de la tabla y del POJO al mismo tiempo que
se escribe el código.
Seteo de ambiente para Hibernate Annotations

En primer lugar es necesario estar utilizando la JDK 5.0 o superior.

Segundo, es necesario tener referenciados a nuestro CLASSPATH los JARs


relacionados a la utilización de Annotations, estos JARS se encuentran dentro del
grupo de JARs definido como “required” que ya agregamos al proyecto cuando
realizamos la configuración inicial.

Ejemplo de Clase con Annotations

Imaginemos que vamos a utilizar la siguiente tabla “AUTOS” para almacenar


nuestros objetos.

Copiar a Clipboard

CREATE TABLE `autos` (

`au_id` int(10) unsigned NOT NULL auto_increment,

`au_marca` varchar(255) NOT NULL default '',

`au_modelo` varchar(255) NOT NULL default '',

PRIMARY KEY (`au_id`)

) ENGINE=MyISAM;

El siguiente sería el mapeo del POJO Auto utilizando annotations:

Copiar a Clipboard

import java.io.Serializable;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import javax.persistence.Table;
@Entity

@Table(name="autos")

public class Auto implements Serializable{

// Atributos

@Id

@GeneratedValue(strategy= GenerationType.AUTO)

@Column(name="au_marca")

private Long id;

@Column(name="au_marca")

private String marca;

@Column(name="au_modelo")

private String modelo;

public Auto() {

public Auto(String unaMarca, String unModelo){

marca = unaMarca;

modelo = unModelo;

public Long getId() {

return id;

public void setId(Long id) {

this.id = id;
}

public String getMarca() {

return marca;

public void setMarca(String marca) {

this.marca = marca;

public String getModelo() {

return modelo;

public void setModelo(String modelo) {

this.modelo = modelo;

public String toString(){

return id + " - " + marca + " - " + modelo;

A continuación explicaremos las Annotations que se utilizaron en el ejemplo:

1) En primer lugar, Hibernate nos permite colocar las annotations sobre nuestras
properties, o sobre los accesors. En el primer caso, accederá a nuestros atributos
directamente y en el segundo a través de los getters y setters.

La diferencia entre estas opciones puede verse en el siguiente ejemplo:

Supongamos que dos campos de nuestra clase se mapean sobre uno solo de la tabla
correspondiente, el típico caso de Persona, con nombre y apellido. Si usamos
annotations sobre accesors, podríamos lograr que el setter reciba un String, lo parsee
y lo reparta entre los atributos nombre y apellido de mi clase. El getter, por su parte,
concatenaría nombre + apellido y lo devolvería a Hibernate para que lo cargue en la
columna correspondiente. A su vez, basándonos en los getters logramos un mayor
control pudiendo agregar la información que deseemos a la columna correspondiente.

Por defecto, Hibernate accederá a los valores de nuestras propiedades de la forma en


que lo indique la annotation @Id. Si ésta se encuentra en una property, lo hará
siempre a través de properties. De lo contrario, a través de accesors.

2) @Entity: A partir de esta annotation indicamos a Hibernate que nuestra clase es


persistente. Se recomienda el uso de la annotation Entity de JPA, la de Hibernate
suele traer dificultades. Una vez declarada persistente nuestra clase, todos sus
atributos lo serán también por default.

3) @Table(name = "ITEM"): A través de esta annotation especificamos un nombre


de tabla en caso de diferir al de nuestra clase. De no indicarlo, Hibernate tomará
como nombre de la tabla asociada, el mismo que el de la clase. Esta es una
convención, y constituye un ejemplo sencillo del concepto "convention over
configuration".

4) @Id: A través de esta annotation indicamos que la property mapeada será la PK de


nuestra tabla. En este caso se trata de una clave simple.

5) @GeneratedValue: Especifica la estrategia de generación de Ids. Hay varias


opciones para generadores. En nuestro caso, al no acompañarla de parámetros
adicionales, su valor será AUTO, que decide una estrategia de generación de Ids
conveniente dependiendo del motor subyacente. (ej. Identity).

6) @Column(name = "ITEM_ID"): En este caso la annotation es opcional, si


pretendemos que la columna de la tabla correspondiente se llame igual que la
property. Aquí valen las mismas consideraciones que en el caso de @Table.

Creación de una clase Tester

La clase TestAutos

Se utilizará la clase TestAutos para lograr hacer el “hola mundo” con las operaciones
básicas.

A continuación se presenta la clase TestAutos:

Copiar a Clipboard

public class TestAutos {


public static void main(String[] args)

insertarAuto();

consultarAutos();

modificarAuto();

consultarAutos();

eliminarAuto();

consultarAutos();

Los métodos insertarAuto(), consultarAutos(), modificarAuto() y eliminarAuto() son


métodos de clase pertenecientes a la clase TestAutos, y se encuentran en esta clase –
solo por ahora – con fines estrictamente didácticos.

El cuerpo de los métodos queda detallado a continuación.

La interfaz org.hibernate.Session

Para poder trabajar con la base de datos resulta necesario obtener una sesión, es decir
un objeto que tiene implementado a través de su clase la interfaz Session. Para
conseguir una sesión, es necesario tener configurado Hibernate: database driver, url
connection, user, password.

También es necesario registrar todos los POJOs de mapeo a utilizar. La forma de


configurar hibernate, registrar los mappers y obtener una sesión es la siguiente:

Copiar a Clipboard

Configuration config = new Configuration();

// Registra las clases POJO en la configuracion


config.addAnnotatedClass(UnaClase.class);

// Establece las propiedades de configuracion

config.setProperties(System.getProperties());

// Guarda la fabrica de sesiones

StandardServiceRegistryBuilder builder = new


StandardServiceRegistryBuilder().applySettings(config.getProperties());

factory = config.buildSessionFactory(builder.build());

// Instancia una sesion de trabajo

Session session = factory.openSession();

Registración de propiedades y mappers

Tanto la registración de propiedades de Hibernate como la registración de los


mappers deben llevarse a cabo cuando comienza la aplicación, y deberá realizarse
una única vez.

En general, es conveniente utilizar una clase que resuelva esta problemática:

Copiar a Clipboard

public class EducacionITSessionManager{

public static Session getSession()throws HibernateException{

Configuration config = new Configuration();

// Registra los mappers en la configuracion

registerMappers(config);

// Establece las propiedades de configuracion

config.setProperties(getHibernateProperties());

// Guarda la fabrica de sesiones


StandardServiceRegistryBuilder builder = new
StandardServiceRegistryBuilder().applySettings(config.getProperties());

factory = config.buildSessionFactory(builder.build());

// Retorna una sesion de trabajo

return factory.openSession();

private static Properties getHibernateProperties(){

Properties props = new Properties();

props.put("hibernate.connection.driver_class",

"com.mysql.jdbc.Driver");

props.put("hibernate.connection.url",

"jdbc:mysql://localhost/hibernate");

props.put("hibernate.connection.username",

"root");

props.put("hibernate.connection.password", "");

props.put("hibernate.dialect",

"org.hibernate.dialect.MySQLDialect");

return props;

private static void registerMappers(Configuration config) throws


MappingException{

config.addAnnotatedClass(ar.com.educacionit.hibernate.entities.Auto.class);

}
Cómo realizar una Inserción

Para insertar un auto en la tabla autos se desarrollara el método insertarAuto(),


presentado a continuación:

Copiar a Clipboard

public static void insertarAuto(){

Auto a1 = new Auto("Fiat", "2000");

Session session =

EducacionITSessionManager.getSession();

Transaction tx = session.beginTransaction();

session.save(a1);

tx.commit();

session.close();

Cómo realizar una Consulta

Para consultar la tabla autos se desarrollara el método consultarAutos(), presentado a


continuación:

Copiar a Clipboard

public static void consultarAutos()

Iterator<Auto> it = getAutos().iterator();

while( it.hasNext() )

Auto a = it.next();

System.out.println(a);
}

private static List getAutos()

Session session =

EducacionITSessionManager.getSession();

List losAutos =

session.createQuery("FROM entities.Auto").list();

session.close();

return losAutos;

Cómo realizar una Actualización

Para actualizar un auto correspondiente a la tabla autos, tal que el auto tenga como
id=1, se desarrollara el método modificarAuto(), presentado a continuación:

Copiar a Clipboard

public static void modificarAuto()

Auto a1 = new Auto("Mercedes", "2002");

a1.setId(1L);

Session session =

EducacionITSessionManager.getSession();

Transaction tx = session.beginTransaction();

session.update(a1);

tx.commit();
session.close();

Cómo realizar una Eliminación

Para eliminar un auto correspondiente a la tabla autos, tal que el auto tenga como
id=1, se desarrollara el método eliminarAuto(), presentado a continuación:

Copiar a Clipboard

public static void eliminarAuto()

Auto a1 = new Auto();

a1.setId(7L);

Session session =

EducacionITSessionManager.getSession();

Transaction tx = session.beginTransaction();

session.delete(a1);

tx.commit();

session.close();

CLASE 2

LAS INTERFACES CLAVE

La clase org.hibernate.cfg.Configuration
La implementación de la interfaz Configuration es utilizada para configurar
Hibernate. Se utiliza una instancia de Configuration para establecer tanto los
mappers como las propiedades de Hibernate.

A partir de este objeto se genera un objeto SessionFactory.

La interfaz org.hibernate.SessionFactory

La implementación de la interfaz SessionFactory representa a una fábrica de


sesiones, que se encarga de crear nuevas sesiones con cada solicitud.

Existe un único objeto de este tipo en la aplicación (por defecto), aunque se puede
configurar más de uno en caso de necesitar accesos a más de una base de datos.

La interfaz org.hibernate.Session

Los objetos pertenecientes a la clase que implementa Session son los más utilizados
en una aplicación de Hibernate. Representa una unidad de trabajo y pueden ser
comprendidos como la combinación entre una conexión y una transacción.

La aplicación crea y destruye sesiones constantemente.

La interfaz org.hibernate.Query

Los objetos pertenecientes a la clase que implementa Query se utilizan para realizar
consultas contra la base de datos. Permite construir una consulta y ejecutarla.

Las consultan pueden ser escritas en HQL (Hibernate Query Language) o en SQL
nativo. Se utiliza en conjunto con un objeto del tipo Criteria.

La interfaz org.hibernate.Transaction

Representa el concepto de transacción en forma transparente para el cliente. Abstrae


el código de la aplicación de la implementación del modelo transaccional

El paquete org.hibernate.classic

El paquete org.hibernate.classic contiene la mayoría de las clases utilizadas en


versiones anteriores de Hibernate. Estas clases no son utilizadas en la última versión.

Las clases de la última versión estable quedan ubicadas en el paquete org.hibernate

LOGGING

Qué es
Hibernate tiene la capacidad de mostrar cómo va realizando las operaciones de
ABMC, es decir el código SQL que va ejecutando. Por ejemplo, al utilizar un objeto
Query y ejecutarlo, podemos ver la consulta SQL que está lanzando Hibernate al
DBMS.

Desde la versión 4.0 Hibernate utiliza las librerías de JBoss Logging, al igual que
SLF4J y Jakarta’s commons-logging, JBoss es un “logging bridge” que provee
integración con numerosos frameworks de logging.

JBoss Logging puede trabajar con los siguientes frameworks de logging:

 JBoss LogManager
 Log4j 2
 Log4j 1
 Slf4j
 JDK logging

La documentación oficial del logging de Hibernate se puede observar en:

http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html

Categorías de interés

Category Description
Log all SQL statements as they are executed with
org.hibernate.SQL
through JDBC
Log all values as they are bound to JDBC
org.hibernate.type.descriptor.sql
parameters and extracted from JDBC results
Log all SQL DDL statements as they are executed
org.hibernate.tool.hbm2ddl during execution of any of the schema migration
tools
Log the state of all entities (max 20 entities)
org.hibernate.pretty
associated with the session at flush time
org.hibernate.cache Log all second-level cache activity
org.hibernate.hql.internal.ast.ASTLog HQL and SQL ASTs during query parsing
Log everything. This is a lot of information but it
org.hibernate
is useful for troubleshooting

Para esto, se deberá tener los siguientes .jars en el CLASSPATH:

 commons-logging-x.x.x.jar
 log4j-x.x.x.jar
“x” corresponde a la versión correspondiente del .jar. Ambos vienen junto con todos
los jars de Hibernate. Se deberá también agregar un archivo de configuración
llamado log4j.properties en el CLASSPATH.

El archivo log4j.properties

El archivo log4j.properties determina la configuración de logger a utilizar. Hibernate


utiliza el Apache log4j, que utiliza el archivo log4j.properties. Una configuración
sencilla de este archivo, para divisar el log en la consola, podría ser:

Copiar a Clipboard

## direct log messages to stdout ###

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target=System.out

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=

%d{ABSOLUTE}?%5p %c{1}:%L - %m%n

### root logger option ###

log4j.rootLogger=warn, stdout

### Hibernate logging options ###

log4j.logger.net.sf.hibernate=debug

### log JDBC bind parameters ###

log4j.logger.net.sf.hibernate.type=debug

### log PreparedStatement cache activity ###

log4j.logger.net.sf.hibernate.ps.PreparedStatementCache=

debug

La propiedad hibernate.show_sql
La propiedad hibernate.show_sql se utiliza para poder visualizar las consultas que va
realizando Hibernate. Para configurarla, se deberá agregar la propiedad
hibernate.show_sql en el archivo de configuración utilizado.

Si deseamos configurarlo dentro de la clase que administra la sesión, será necesario


agregarla de la siguiente manera:

Copiar a Clipboard

Properties props = new Properties();

props.put("hibernate.connection.driver_class",

"com.mysql.jdbc.Driver");

props.put("hibernate.connection.url",

"jdbc:mysql://localhost/hibernate");

props.put("hibernate.connection.username", "root");

props.put("hibernate.connection.password", "root");

props.put("hibernate.dialect",

"org.hibernate.dialect.MySQLDialect");

props.put("hibernate.show_sql", "true");

ADMINISTRACIÓN DE SESIONES

Qué es un singleton

El Singleton es un patrón de diseño que tiene como objetivo construir una única
instancia de un objeto. Al intentar obtener un nuevo objeto solicitándolo a la clase, la
misma clase entrega al cliente una instancia previamente creada.

Asegura que habrá una única instancia de la clase que tiene aplicado este patrón.

Porqué utilizar un singleton

La clase que se utiliza como administrador de sesiones está constantemente


“configurando” Hibernate. Por cada solicitud de una sesión se registran los mappers
y las propiedades de Hibernate.
Estas actividades deberían ejecutarse una única vez ya que son siempre las mismas.
La solución a la problemática es aplicarle el patrón de diseño Singleton a la clase que
maneja las sesiones.

CLASE 3

HQL: HIBERNATE QUERY LANGUAGE

Qué es

HQL es un lenguaje de consulta muy parecido a SQL, es una de las propuestas de


Hibernate para “conversar” con la base de datos. Es un SQL propietario de Hibernate
y orientado a objetos. Permite ejecutar consultas SQL generadas automáticamente.

Utiliza en sus consultas clases y atributos, a diferencia de SQL que utiliza tablas y
campos. Si bien no es un lenguaje case-sentitive, las clases y los atributos son case-
sentitive, así que hay que tener cuidado a la hora de armar consultas.

A diferencia de SQL, al ser orientado a objetos, comprende conceptos como


herencia, polimorfismo y asociaciones.

Por qué utilizarlo

Al utilizar HQL no resulta necesario escribir SQL, se podría no tener conocimientos


de SQL para la utilización de Hibernate, aunque es recomendable.

Permite realizar consultas en forma de objetos. El resultado de las consultas vienen


ya construidas como el objeto con el que es necesario trabajar. Por ejemplo, si se
consulta la tabla autos, devuelve N instancias de la clase Auto en una colección.

Las consultas escritas en HQL son independientes de la implementación de SQL, lo


que resulta en que una aplicación desarrollada con Hibernate sea multi base de datos.

HQL comprende la utilización de herencia, polimorfismo y asociaciones.

Cláusulas

From

La cláusula from es la cláusula más sencilla, se utiliza para obtener todos los ítems
de una tabla.

La forma de utilizarlo en SQL sería:


Copiar a Clipboard

SELECT * FROM tabla;

La forma de utilizarlo en HQL sería:

Copiar a Clipboard

String strQuery = "FROM paquete.Clase";

Query qry = session.createQuery(strQuery);

List<Clase> lista = qry.list();

Iterator<Clase> it = lista.iterator();

while( it.hasNext() ) {
Clase c = it.next();
System.out.println("Campo1: " + c.getAtrib1() );
System.out.println("Campo2: " + c.getAtrib2() );

select

La cláusula select se utiliza para seleccionar qué datos traer de una o más tablas.
Generalmente se utiliza cuando se necesita traer datos de atributos de distintos
objetos.

A diferencia de la cláusula FROM, al ejecutar un SELECT retorna un array de


Objects (Object[]).

Esto se debe a que se podrían traer datos de más de una tabla, con lo cual sería
imposible determinar a que objeto pertenece.

En SQL sería:

Copiar a Clipboard

SELECT t1.campo1, t2.campo1 FROM tabla1 t1, tabla2 t2;

En HQL sería:

Copiar a Clipboard

String strQuery =
"SELECT c1.atrib, c2.atrib FROM paquete.Clase

c1, paquete.Clase2 c2";

Query qry = session.createQuery(strQuery);

Iterator it = qry.iterate();

while( it.hasNext() ) {
Object[] row = (Object[]) it.next();
System.out.println("Atrib_c1: " + row[0]);
System.out.println("Atrib_c2: " + row[1]);

where

La cláusula where se utiliza para determinar un criterio de búsqueda.

En SQL sería:

Copiar a Clipboard

SELECT campo1 FROM tabla1 WHERE campo1 = 10;

En HQL sería:

Copiar a Clipboard

String strQuery =

"FROM paquete.Clase c1 WHERE c1.atrib = 10";

Query qry = session.createQuery(strQuery);

Iterator<Clase> it = qry.iterate();

while( it.hasNext() ) {
Clase unaClase = it.next();
System.out.println("Atrib: " + unaClase.atrib);
}

Otro ejemplo en SQL sería:

Copiar a Clipboard
SELECT campo1 FROM tabla1 WHERE campo2 LIKE ‘A%’;

En HQL sería:

Copiar a Clipboard

String strQuery =

"FROM paquete.Clase c1 WHERE c1.atrib LIKE ‘A%’ ";

Query qry = session.createQuery(strQuery);

Iterator<Clase> it = qry.iterate();

while( it.hasNext() ) {
Clase unaClase = it.next();
System.out.println("Atrib: " + unaClase.atrib);
}

group by

La cláusula group by se utiliza para agrupar información por algún criterio.

En SQL sería:

Copiar a Clipboard

SELECT SUM(campo1) FROM tabla1 GROUP BY campo2;

En HQL sería:

Copiar a Clipboard

String strQuery =

"SELECT sum(c1.atrib1)

"FROM paquete.Clase c1 GROUP BY c1.atrib2";

Query qry = session.createQuery(strQuery);

Iterator it = qry.iterate();
while( it.hasNext() ) {
Object[] row = (Object[]) it.next();
System.out.println("Suma: " + row[0]);
}

order by

La cláusula order by se utiliza para ordenar información por algún atributo (campo).

En SQL sería:

Copiar a Clipboard

SELECT * FROM tabla1 ORDER BY campo X;

En HQL sería:

Copiar a Clipboard

String strQuery =

"FROM paquete.Clase c1 ORDER BY c1.atrib";

Query qry = session.createQuery(strQuery);

Iterator<Clase> it = qry.iterate();

while( it.hasNext() ) {
Clase unaClase = it.next();
System.out.println("Atrib: " + unaClase.atrib);
}

Utilización de funciones

Introducción

Es posible utilizar las funciones propias de SQL. Están disponibles las funciones
típicas de ANSI-SQL:

 avg()
 sum()
 min()
 max()
 count(*)
También está disponible la palabra clave DISTINCT

Por ejemplo, el uso de la función SUM() en SQL sería:

Copiar a Clipboard

SELECT sum(campo1) FROM tabla1;

En HQL sería:

Copiar a Clipboard

String strQuery =

"SELECT sum(atrib1) FROM paquete.Clase";

Query qry = session.createQuery(strQuery);

Float suma = (Float) qry.uniqueResult();

System.out.println("Suma total " + suma);

Todas las funciones se manejan de forma similar.

count

count( [ distinct | all ] object | object.property )

sum

sum ( [ distinct | all ] object.property)

avg

avg( [ distinct | all ] object.property)

max

max( [ distinct | all ] object.property)

min

min( [ distinct | all ] object.property)


Consultas SQL Nativas

Qué es una consulta nativa

A veces resulta necesario lanzar consultas específicas de SQL. Para esto es necesario
utilizar el SQL nativo, es decir SQL puro y/o SQL propietario del DMBS que se está
utilizando.

A diferencia del uso de HQL que se realizaba con el método createQuery(), para
utilizar SQL nativo es necesario emplear el método createSQLQuery().

La desventaja que presenta esta estrategia es que generalmente se utiliza SQL


propietaria, que si bien por un lado potencia el uso del DBMS, por otro lado tiene
como consecuencia que la aplicación deje de ser multi base de datos.

El método createSQLQuery()

El método createSQLQuery() es el método perteneciente a la interfaz Query utilizado


para realizar consultas de SQL nativo. La forma de utilización es la siguiente:

Copiar a Clipboard

List losAutos = session

.createSQLQuery("SELECT * FROM autos")

.addEntity(Auto.class)

.list();

HQL: HIBERNATE QUERY LANGUAGE

Valores por parámetro

A veces resulta necesario construir consultas dinámicas con valores que se


determinan en tiempo de ejecución. El objetivo es establecer los valores de los
parámetros a consultar a medida que son obtenidos.

Para esto, es necesario construir una consulta con marcas donde se reemplazan los
valores.

Por ejemplo:

Copiar a Clipboard

“FROM paquete.Clase WHERE atrib = :parámetro”


Luego será necesario reemplazar el parámetro con el método setString() en caso de
ser una cadena de caracteres o el setter que resulte necesario en caso de ser otro tipo
de parámetro.

La forma de utilización es la siguiente:

Copiar a Clipboard

Query qry = s.createQuery(

"FROM paquete.Clase WHERE atrib = :p1 ");

qry.setString("p1", unValor);

List lista = qry.list();

Paginación

Definición

La paginación es la posibilidad de trabajar con parte del resultado que satisface una
consulta. Muchas veces resulta imposible mostrar una cantidad importante de
registros, y lo que se hace es mostrarlos agrupados por una cantidad. A esta cantidad
de registros se la denomina “Página”.

Hibernate posee una propuesta de paginación a través de métodos pertenecientes a la


clase org.hibernate.Query

Estos métodos son el setMaxResults() y el setFirstResult().

El método setMaxResults()

El método setMaxResults() establece que cantidad de registros traer. Representa al


concepto de página.

La forma de utilización es:

Copiar a Clipboard

int tamanoPagina = 50;

Query qry = s.createQuery(

"FROM paquete.Clase”);

qry.setMaxResults(tamanoPagina);
List lista = qry.list();

El método setFirstResult()

El método setFirstResult() establece cuál será el primer registro a traer, esto se debe a
que a veces resulta necesario traer del registro 0 al 100, o tal vez del registro 500 al
600.

El primer registro tiene el número cero.

La forma de utilización es:

Copiar a Clipboard

int primerRegistroATraer = 122;

Query qry = s.createQuery(

"FROM paquete.Clase”);

qry.setFirstResult(primerRegistroATraer);

List lista = qry.list();

Named Queries

Qué es un Named Query

Los Named queries en hibernate son una forma de agrupar sentencias HQL en una
única locación para luego referirse a ellos cuando sea necesario. Esto ayuda mucho a
limpiar el código porque las sentencias HQL ya no se encuentran mezcladas entre el
código de nuestras clases.

Además de esto, debajo se encuentran otras ventajas de utilizar named queries:

Fail fast: La sintaxis es verificada en el momento en que la session factory es creada,


ocasionando que la aplicación falle en caso de un error.

Reutilización: Pueden ser accedidos y utilizados desde varias clases lo que


incrementa la re-usabilidad.

El Named Query mapping

La definición de un Named query tiene dos atributos importantes:


 name: El nombre del query a partir del cual será localizado utilizando la
sesión Hibernate.
 query: La sentencia HQL que se ejecutará en la base de datos.

Para crear un Named Query, es necesario utilizar la annotation @NamedQueries con


la siguiente sintaxis:

Copiar a Clipboard

@NamedQueries({

@NamedQuery(

name = "obtenerAutosCaros",

query = "FROM ar.com.educacionit.hibernate.entities.Auto a WHERE a.precio >


1000"

})

El método getNamedQuery()

El método getNamedQuery() es el método utilizado para obtener la consulta


almacenada en el archivo de mapeo.

La forma de utilización es la siguiente:

Copiar a Clipboard

List lista = session.getNamedQuery("nombreQuery")

.list();

CLASE 4

MAPEO DE HERENCIA DE CLASES

Problemática
No es posible modelar la herencia en un modelo de datos, lo cual produce un gap
entre el modelo relacional y el modelo de objetos. SQL no provee soporte para
herencia.

Soluciones

Una tabla por clase concreta

Esta propuesta es la solución más sencilla. Se utiliza una clase concreta por cada
tabla. Por cada atributo de la clase, existe un campo en la tabla correspondiente.
Es la solución ideal si no se tienen en cuenta conceptos como herencia y
polimorfismo. Descarta el uso de relaciones de herencia y polimorfismo del modelo
relacional.

Una tabla por subclase

Representa la herencia a través de claves foráneas, ya que por cada subclase existe
una tabla. Cada subclase, incluyendo clases abstractas, tiene su propia tabla. Cada
tabla contiene campos solo por cada atributo que no es heredado.
La clave primaria es a su vez la clave foránea correspondiente a la superclase. Para
obtener los datos de una subclase, es necesario hacer un join entre la tabla que
representa a la superclase y la tabla que representa a la subclase.

Una tabla por jerarquía de clases

Se construye una tabla por cada jerarquía de clases, donde la tabla posee un campo
por cada atributo de la jerarquía de clases. Adicionalmente, se utiliza un campo de
tipo discriminador (discriminator) para establecer qué tipo de clase corresponde a
cada registro.
Integra el concepto de polimorfismo y es la mejor estrategia en términos de
performance y simplicidad.
La única restricción que posee es que los atributos de las subclases deben quedar
como campos que pueden estar en NULO.
Para confeccionar esta relación, dentro del modelo relacional será necesario construir
la siguiente tabla denominada transportes:
Copiar a Clipboard

@Entity

@Table(name = "transportes")

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(

name="tr_discriminador",

discriminatorType=DiscriminatorType.STRING

@DiscriminatorValue(value="T")

public class Transporte {

@Id

@GeneratedValue

@Column(name = "tr_id")

private Long transporteId;

@Column(name = "tr_largo")

private int largo;


@Column(name = "tr_ancho")

private int ancho;

// Constructors and Getter/Setter methods,

}
-----------------------------------------------------
@Entity
@Table(name="transportes")
@DiscriminatorValue("A")
public class Auto extends Transporte {

@Column(name="tr_au_patente")
private String patente;
// Constructors and Getter/Setter methods,
}
El código presentado en negrita es el código necesario para representar la relación de
herencia dentro de hibernate. El discriminador se utiliza para guardar un valor en la
tabla que identificará si un registro corresponde a un Transporte o a un Auto.
La clase Transporte es la superclase, por tal motivo los valores para las
annotations @Inheritance y @DiscriminatorColumn se definen en la misma.
@Inheritance – Define la estrategia a utilizar, se define en la superclase de la
jerarquía. Los valores posibles son JOINED, SINGLE_TABLE y
TABLE_PER_CLASS.
@DiscriminatorColumn – Es utilizada para definir la columna que será utilizada
como discriminador, se utiliza en las estrategias SINGLE_TABLE and JOINED. La
estrategia de mapeo y la columna del discriminador se especifican solamente en la
superclase de la jerarquía.
Si la annotation para @DiscriminatorColumn no está definida, y una columna para el
discriminador es necesaria, los valores por defecto son "DTYPE" para el nombre
y DiscriminatorType.STRING para el tipo.
@DiscriminatorValue – Es utilizada para especificar el valor de la columna
discriminadora para las entidades de cada tipo. La
annotation DiscriminatorValue puede ser solamente especificada en una clase
concreta.

CLASE 5

ASOCIACIONES

Existen tres tipos de relaciones que generalmente podemos encontrar en un modelo


relacional:

 One to Many / Many to One.


 One to One.
 Many to Many

One to Many / Many to One

En esta clase de relaciones existe una entidad que tiene como atributo una colección
de entidades de otro tipo. A la entidad que posee la colección se la conoce como
extremo one. La entidad a la que pertenecen los elementos de la colección se la
conoce como extremo many. La entidad del extremo many se relacionará como
máximo con una entidad del extremo one.

El siguiente ejemplo nos permite comprender cómo funcionan estas relaciones.

Ítem y Factura:

 Una Factura podrá tener N ítems (Extremo One).


 Cada Ítem pertenecerá como máximo a una Factura. (Extremo Many).

Las clases para este modelo serían:


Factura

Copiar a Clipboard

public class Factura {

private Long codigo = null;

private Set<Item> items = new HashSet<Item>();

Item

Copiar a Clipboard

public class Item {

private Long id = null;

private Factura factura;

private Long cantidad;

private String nombreProducto;

Para definir cómo se mapearán las clases hay ciertos aspectos del negocio que
primeramente deberíamos tener definidos.
1. En nuestro sistema agregaremos los ítems a la factura, lo que es más común a
la hora de trabajar con objetos complejos, en lugar de a cada ítem setearle la
factura asociada.
2. El ciclo de vida del Ítem depende del de la factura. Si no existe la factura no
tienen sentido sus ítems. Si se elimina una factura, se eliminan también sus
ítems.
3. Acceso a la información: siempre que traigo los datos de una factura traigo
los datos de sus ítems, o accedo a los datos de los ítems en una segunda
instancia. Existen las dos posibilidades, analizaremos las dos situaciones con
las opciones que nos da Hibernate.

Existen otros aspectos que irán surgiendo a medida que avancemos, no obstante por
ahora podemos comenzar a atacar el mapeo de los objetos.

Empezamos con Item:

Copiar a Clipboard

@Entity

@Table(name = "items")

public class Item {

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

@Column(name = "it_id")

private Long id = null;

@ManyToOne

@JoinColumn(name = "fac_id")

private Factura factura;

@Column(name = "it_cantidad")
private Long cantidad;

@Column(name = "it_descripcion")

private String nombreProducto;

Este mapeo se lo conoce como mapeo unidireccional muchos a uno.

Observaciones:

1) @ManyToOne: Este es el extremo “Many” de la relación. La tabla items tendrá


una FK a la tabla facturas. Esta annotation puede contener diferentes configuraciones
con respecto al tipo de cascading y el fetching, las cuales abordaremos más adelante.

2) @JoinColumn(name = "fac_id"): Indica el nombre de la columna de la tabla a


través de la cual se establece la relación .

Con este simple mapeo sería suficiente para establecer la relación entre ambas
entidades, no obstante nos puede interesar recorrer la relación en forma bidireccional.

Para ello mapearemos el otro extremo de la relación.

Copiar a Clipboard

@Entity

@Table(name = "facturas")

public class Factura {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)

@Column(name = "fac_id")

private Long codigo = null;

@OneToMany

@JoinColumn(name = "fac_id")

private Set<Item> items = new HashSet<Item>();

Observaciones:

1) @OneToMany: Explicita la relación desde el extremo “one”. Esta annotation


puede contener diferentes configuraciones con respecto al tipo de cascading y
el fetching.

De esta forma tenemos el mapeo básico de nuestras entidades, no obstante todavía no


tuvimos en cuenta los aspectos conversados al inicio del mapeo.

El punto uno se refería a la forma en que se iría armando el objeto compuesto, como
los ítems se agregarían a la factura:

Copiar a Clipboard

Factura f = new Factura();

Item i = new Item();

f.getItems().add(i);

El objetivo sería lograr que cuando se persista el objeto factura también se persistan
los ítems y queden relacionados mediante la FK.
Por defecto Hibernate asume que los dos extremos de la relación se sincronizan con
la base de datos. Con lo cual, tanto si agrego un ítem a una factura o viceversa, este
cambio se persistirá en la base de datos.

Este comportamiento puede ser modificado mediante el concepto de mappedBy para


lograr una solución más genérica aplicable a la mayoría de los casos.

MappedBy

Con esta configuración podemos lograr que las modificaciones que se realicen en la
colección del extremo One no se sincronicen con la BD.

Copiar a Clipboard

@OneToMany(mappedBy="factura")

Esto indica que el atributo persistente es “factura” en la clase Item.

Con esto logramos que :

Copiar a Clipboard

factura.getItems().add(item);

Sea ignorado por la base de datos a la hora de persistir.

Copiar a Clipboard

Item.setFactura(factura);

Sería persistido en la base de datos, llegado el momento de persistir los cambios.

Cascading

Este tema se encuentra relacionado con el segundo aspecto a tener en cuenta que
planteamos en el inicio.

Copiar a Clipboard
Factura fac = new Factura();

Item it = new Item();

fac.getItems().add(it);

DB.save(fac);

El ciclo de vida de ambos objetos está relacionado, tanto si agrego como si remuevo
un ítem en el momento de persistir la información en la DB todos los cambios
quedan reflejados.

Para ello utilizamos el parámetro Cascade, este parámetro es unidireccional, puede


declararse en ambos extremos.

Copiar a Clipboard

@OneToMany(cascade = CascadeType.ALL, mappedBy = "factura")

private Set<Item> items = new HashSet<Item>();

CascadeType.ALL indica que se propaguen todas operaciones posibles desde la


entidad fuente al destino.

Un último aspecto a tener en cuenta con respecto al cascadeo es la existencia de


objetos huérfanos, para lograr que al eliminar la relación entre un ítem y una factura
el registro del ítem se elimine de la base de datos es necesario configurar la opción
llamada delete-orphan:

Copiar a Clipboard

@OneToMany(cascade =
CascadeType.ALL,mappedBy="factura", orphanRemoval=true)

Fetching
El tercer punto que abordamos es la forma en que vamos a buscar la información a la
base de datos, la diferencia radica en que si vamos a buscar una factura a la base de
datos traemos siempre la información de los ítems, o esta información la traemos en
una segunda instancia.

Existen dos opciones:

Lazy: al traer un ítem, no nos traerá aún la factura asociada. Al traer el ítem se
realizará un SELECT y otro cuando se desee obtener la información de la factura.

Eager: realiza un Join entre la tabla ITEM y FACTURA, para poder cargar ambos
objetos en el momento de obtener el ítem desde la Base de datos.

Copiar a Clipboard

@OneToMany(fetch = FetchType.EAGER/LAZY).

Por defecto, el fetching es LAZY para colecciones y EAGER en casos onetomny.

One-To-One

Es una relación simple “uno a uno” donde una tabla tiene una FK a otra.

Ejemplo:

Copiar a Clipboard

@Entity

@Table(name = "CLIENTE")

public class Cliente {

@OneToOne

@JoinColumn(name = "DIR_ID")

private Direccion direccion;

}
@Entity

@Table(name = "DIRECCION")

public class Direccion {

@OneToOne(mappedBy="direccion")

private Cliente cliente;

Many-To-Many

En un modelo relacional las relaciones muchos a muchos se encuentran


representadas con una tabla cruzada entre entidades. En un modelo de objetos no
existen entidades para representar estas tablas cruzadas ya que no es necesario para
poder resolver la problemática.

A la hora de mapear esta relación podemos optar por utilizar dos relaciones many-to-
one, de la forma ya vista, en caso de que tenga sentido para nuestro sistema mapear
la tabla intermedia, ya sea porque posee información adicional o por algún otro
motivo.

Si optamos por ignorar la tabla intermedia podremos mapear una relación many-to-
many.

Imaginemos un Producto, asociado al ítem en una relación one-to-many, y a una


clase Categoría, en una relación many-to-many con la clase Producto.

Para obtener los ítems asociados a la categoría a partir de la misma, en forma


unidireccional:

Copiar a Clipboard

@Entity
@Table(name = "CATEGORIA")

public class Categoria {

@ManyToMany

@JoinTable(name = "CATEGORIA_ITEM",

joinColumns = {@JoinColumn(name = "CAT_ID")},

inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")}

private Set<Item> items = new HashSet<Item>();

Bidireccional:

Copiar a Clipboard

@ManyToMany(mappedBy = "items")

private Set<Categoria> categorias = new HashSet<Categoria>();

CLASE 6

TRANSACCIONES

La interfaz org.hibernate.Transaction
Como se presentó anteriormente, representa el concepto de transacción en forma
transparente para el cliente. Permite trabajar con un conjunto de acciones de forma
atómica, es decir que se realizan todas las acciones o no se realiza ninguna

Abstrae el código de la aplicación de la implementación del modelo transaccional, y


provee los métodos que determinan el manejo de la transacción.

Qué significa Transparent Write Behind

Hibernate no ejecuta cada una de las altas/modificaciones/eliminaciones que


realizamos justo después de realizarlas. Por el contrario, las almacena en el objeto
sesión, el cual luego se encarga de ejecutarlas. Esto se denomina “Transparent Write
Behind”.

Tiene ciertas ventajas, como por ejemplo si modificamos más de una vez un mismo
objeto, no se realizan idas y vueltas a la base de datos sino que se resuelve en
memoria.

También utiliza el concepto de PreparedStatement de JDBC, con lo cual cuando


considera necesario reúne varias queries y las ejecuta todas juntas.

El método load()

El método load() de la session permite obtener una instancia de un objeto desde la


base de datos si se conoce de antemano su identificador. Si no encuentra el objeto en
base de datos, salta una excepción. Si lo encuentra, no lo carga, sino que te devuelve
un proxy del objeto. La carga real se hace cuando intentamos obtener datos de ese
proxy, por lo que incluso pueden saltarnos excepciones después, cuando estemos
usando los métodos get del proxy, ya que es cuando se intentará realmente la carga
del objeto de la base de datos.

Su utilización es la siguiente:

Copiar a Clipboard

Auto a1 = (Auto) sess.load(Auto.class, idAuto);

El método get()

A diferencia del método load(), el método get() se utiliza cuando no se tiene la


certeza de antemano que el objeto a buscar tiene su correspondiente información en
la base de datos. Si lo encuentra devuelve el objeto. En caso de no ser encontrado,
este método simplemente retorna null.

Su utilización es la siguiente:
Copiar a Clipboard

Auto a1 = (Auto) sess.get(Auto.class, idAuto);

El método beginTransaction()

El método beginTransaction() es el método utilizado para determinar el comienzo de


la transacción. Las transacciones se determinan dentro de una sesión, y este método
retorna un objeto del tipo Transaction.

El método flush()

El método flush() es el encargado de sincronizar la sesión con la base de datos. Es


posible llamarlo en forma manual, o llamarlo automáticamente por la sesión cuando
ésta lo considera necesario.

En general, se utiliza a este método cuando:

 se lo invoca explícitamente para obligar la sincronización de datos con la


base de datos
 se invoca al método commit(), que llamará posteriormente al método flush()

A veces también es invocado antes de una consulta, en caso de que algún valor en
memoria afecte el resultado de la consulta

El método setFlushMode()

Es posible establecer el comportamiento de la sincronización con la base de datos a


través de la utilización del método setFlushMode(). Los diferentes modos de realizar
el flush son:

 FlushMode.AUTO, se maneja de forma automática


 FlushMode.COMMIT, se realiza únicamente cuando se llama al método
commit(), y no antes de una consulta
 FlushMode.NEVER, se realiza únicamente cuando se lo llama en forma
explícita

No se recomienda el cambio del modo de trabajo, a menos en casos muy particulares


donde se necesite aumentar la performance. En general, no debería ser necesario
llamar al método flush() manualmente.

El método commit()

El método commit() es el método utilizado para “comprometer los cambios”


realizados. Se encarga de sincronizar los cambios efectuados con la base de datos,
ejecuta todas las modificaciones realizadas durante la transacción.
Una vez ejecutado, no es posible volver atrás los cambios realizados.

El método rollback()

El método rollback() es el método utilizado para deshacer los cambios realizados


previamente. Los cambios que se toman como válidos son todos aquellos que se
realizaron luego de la llamada al método beginTransaction().

Los cambios realizados previos al commit() no tienen efecto si luego del commit() se
llama al método rollback().

Utilización de rollback() y commit()

Copiar a Clipboard

// Variables

Session session = null;

Transaction tx = null;

try {

// Obtiene la session de trabajo

session = EducacionITSessionManager.getSession();

// Comienza la transacción

tx = session.beginTransaction();

// realizar acciones de la transacción

...

...

// Compromete los cambios realizados

tx.commit();
}

catch (Exception e) {

// Vuelve atrás los cambios realizados

if (tx!=null)

tx.rollback();

// Envuelve la excepción de acceso a datos

// Lanza una excepción de negocios

throw new MiExcepcionDeNegocio(e);

finally {

// Cierra la sesión de trabajo

if(session!=null)

session.close();

CLASE 7

HERRAMIENTAS COMPLEMENTARIAS

Introducción

Generación de código con Netbeans

Netbeans cuenta con un conjunto de herramientas que nos permite generar


automáticamente tanto el código Java de los POJOs como las anotaciones necesarias
para el mapeo con la base de datos a partir de la estructura de las tablas creada en una
base de datos.

A continuación enumeramos los pasos necesarios para generar el código utilizando


como ejemplo una base de datos MySQL.

Conexión a la base de datos

El primer paso a realizar es configurar la coneccion a la base de datos desde


NetBeans, para ello nos dirigimos a la solapa de Servicios, hacemos click derecho en
Databases y luego en New Connection.

En la ventana que se abre seleccionamos New Driver


Presionando el botón ADD se despliega un navegador que nos permite seleccionar el
driver de la base de datos a la que nos queremos conectar, en este caso MySQL.
Lo seleccionamos y presionamos OK.

Luego presionamos NEXT.


En este punto ya nos encontramos en condiciones de configurar la base de datos a la
que nos queremos conectar. En Host se ingresa la IP o el DNS Name del servidor
donde se encuentra alojado el RDBMS, Por el puerto en el que se encuentra
publicado el servicio (El puerto por defecto para MySQL es 3306). Database es el
nombre de la base de datos dentro del RDBMS a la cual nos queremos conectar. En
User Name y Password ingresamos las credenciales para conectarse al RDBMS.
Presionando el botón Test Connection podemos probar si los datos ingresados son
válidos, en caso de que el test sea exitoso, debe mostrarse un cartel como el siguiente
en la misma pantalla de configuración.

Ya estamos en condiciones de presionar el botón Finish y podemos observar que en


nuestra solapa de servicios se creó una nueva conexión.
Generación automática de POJOs

Una vez configurado el acceso a la base de datos desde NetBeans, podemos utilizar
el mismo para generar en forma automática el código de los POJOs con las
anotaciones incluidas a partir de las tablas creadas en la base. Para ellos creamos un
nuevo proyecto y haciendo click derecho en cualquier paquete nos dirigimos a la
opción New -> Entity Classes from Database...
Esto desplegará una pantalla donde podemos seleccionar las tablas a partir de las
cuales queremos generar el código.

Seleccionando la conexión a la base de datos previamente configurada, en la lista de


tablas disponibles se observarán todas las tablas existentes. Seleccionamos las que
deseamos utilizar en la lista y utilizando los botones Add y/o Remove definimos
cuales son las tablas a partir de las cuales se va a generar el código. Luego
presionamos el botón Next > .
Destildamos los checks Generate JAXB Annotations y Create Persitence Unit y
presionamos el botón Next > .
Destildamos el check Use Column Names in Relationships y presionamos Finish .
Esto generará como resultado el código de las clases correspondientes a los POJOs,
incluidas las Annotations correspondientes.

Notas a tener en cuenta

Al generar el código automáticamente, según la configuración de NetBeans, en


algunos casos se pueden asignar automáticamente librerías de JPA al proyecto que
pueden tener conflictos con las de Hibernate, revisar las librerías asignadas al
proyecto luego de generar el código.

Otro tema a tener en cuenta es el nombre de las tablas y los campos, Netbeans
nombrará a las clases y a los atributos con los mismos nombres que poseen las tablas
y las columnas en la base de datos, por tal motivo no se recomienda tener las tablas
con nombres en plural y las columnas con nombres que sean aplicables a los
atributos de las clases.-

También podría gustarte