02.11.2014 Views

Tema 3.3: Implementación de la Persistencia con Hibernate - Dpto ...

Tema 3.3: Implementación de la Persistencia con Hibernate - Dpto ...

Tema 3.3: Implementación de la Persistencia con Hibernate - Dpto ...

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>3.3</strong> Implementación <strong>de</strong> <strong>la</strong> <strong>Persistencia</strong><br />

<strong>con</strong> <strong>Hibernate</strong>


Índice<br />

• Introducción<br />

• Mapping objeto/re<strong>la</strong>cional<br />

• Anotación <strong>de</strong> entida<strong>de</strong>s<br />

• Mo<strong>de</strong><strong>la</strong>do <strong>de</strong> re<strong>la</strong>ciones<br />

• Gestión <strong>de</strong> <strong>la</strong> <strong>Persistencia</strong><br />

• API <strong>de</strong> <strong>Persistencia</strong> <strong>de</strong> <strong>Hibernate</strong><br />

• Transacción típica<br />

• Ejemplos<br />

• Configuración e Inicialización <strong>de</strong> <strong>Hibernate</strong><br />

• Lenguaje <strong>de</strong> Consultas<br />

• Implementación <strong>de</strong> DAOs <strong>con</strong> <strong>Hibernate</strong>


Introducción (1)<br />

• <strong>Hibernate</strong> es un mapeador objeto/re<strong>la</strong>cional (ORM), <strong>de</strong> código<br />

abierto, creado por un grupo <strong>de</strong> <strong>de</strong>sarrol<strong>la</strong>dores dirigido por<br />

Gavin King a finales <strong>de</strong> 2001<br />

• Es el ORM más utilizado actualmente – http://www.hibernate.org<br />

• Motivación<br />

• Simplificar <strong>la</strong> programación <strong>de</strong> <strong>la</strong> capa <strong>de</strong> persistencia <strong>de</strong> <strong>la</strong>s<br />

aplicaciones: utilizar JDBC directamente es muy tedioso<br />

• Permite al <strong>de</strong>sarrol<strong>la</strong>dor utilizar objetos persistentes en lugar <strong>de</strong><br />

manipu<strong>la</strong>r directamente los datos <strong>de</strong> <strong>la</strong> BD<br />

• Asocia c<strong>la</strong>ses persistentes <strong>con</strong> tab<strong>la</strong>s <strong>de</strong> una BD re<strong>la</strong>cional,<br />

haciendo transparente <strong>la</strong> API <strong>de</strong> JDBC y el lenguaje SQL<br />

• Soporta re<strong>la</strong>ciones entre objetos y herencia<br />

• No oculta el tipo <strong>de</strong> BD (re<strong>la</strong>cional), pero sí <strong>la</strong> BD <strong>con</strong>creta (Oracle,<br />

MySQL, PostgreSQL, etc.)


• API dual<br />

Introducción (y 2)<br />

• Implementa <strong>la</strong> API <strong>de</strong> <strong>Persistencia</strong> <strong>de</strong> Java (JPA)<br />

• Proporciona una API propia, que será <strong>la</strong> usada en los<br />

ejemplos <strong>de</strong>l curso, porque en aplicaciones reales pue<strong>de</strong><br />

no llegar <strong>con</strong> <strong>la</strong> API <strong>de</strong> JPA<br />

• Lenguaje <strong>de</strong> Consultas:<br />

• HQL (<strong>Hibernate</strong> Query Language)<br />

• Lenguaje <strong>de</strong> <strong>con</strong>sultas <strong>de</strong> búsqueda y borrados/actualizaciones<br />

en masa, <strong>con</strong> semántica orientada a objetos<br />

• Sintaxis parecida a SQL<br />

• Un sub<strong>con</strong>junto está estandarizado como JPA QL<br />

• API <strong>de</strong> Programación para QBC (Query By Criteria)<br />

• Si es necesario, permite <strong>la</strong>nzar <strong>con</strong>sultas SQL


Mapping objeto/re<strong>la</strong>cional (1)<br />

• Para que <strong>Hibernate</strong> sepa cómo mapear <strong>la</strong>s instancias <strong>de</strong> c<strong>la</strong>ses<br />

persistentes (l<strong>la</strong>madas “entida<strong>de</strong>s”) a <strong>la</strong> BD es necesario<br />

especificar metainformación como nombre <strong>de</strong> <strong>la</strong> tab<strong>la</strong>, nombres<br />

<strong>de</strong> <strong>la</strong>s columnas a <strong>la</strong>s que mapear los atributos, etc.<br />

• Alternativas para especificar <strong>la</strong> metainformación <strong>de</strong> mapping<br />

• Usar ficheros XML<br />

• El código quedaría “limpio” <strong>de</strong> referencias a elementos <strong>de</strong> <strong>la</strong> BD<br />

• Usar Anotaciones sobre <strong>la</strong> c<strong>la</strong>se persistente<br />

• <strong>Hibernate</strong> mantiene los nombres <strong>de</strong> anotaciones JPA para <strong>la</strong>s comunes<br />

y <strong>de</strong>fine otras adicionales<br />

• El código tiene cableado nombres <strong>de</strong> tab<strong>la</strong>s, columnas, etc.<br />

• Sin embargo, es una solución más sencil<strong>la</strong>, y en <strong>con</strong>secuencia,<br />

representa el enfoque a<strong>con</strong>sejado<br />

• Es posible especificar <strong>la</strong> información <strong>de</strong> mapping en anotaciones y<br />

ficheros <strong>de</strong> <strong>con</strong>figuración simultáneamente para una misma entidad<br />

• La información <strong>de</strong> los ficheros <strong>de</strong> <strong>con</strong>figuración tiene preferencia sobre<br />

<strong>la</strong>s anotaciones => se pue<strong>de</strong> sobre-escribir <strong>la</strong> información dada <strong>con</strong> <strong>la</strong>s<br />

anotaciones y/o añadir más información sin necesidad <strong>de</strong> tocar código


Mapping objeto/re<strong>la</strong>cional (y 2)<br />

• Para realizar <strong>la</strong> persistencia, es posible:<br />

• Utilizar tab<strong>la</strong>s existentes<br />

• Única opción cuando <strong>la</strong>s tab<strong>la</strong>s ya estaban creadas<br />

• Más <strong>con</strong>trol sobre <strong>la</strong>s tab<strong>la</strong>s e índices que se usarán<br />

• Por motivos pedagógicos, en los ejemplos <strong>de</strong>l curso se<br />

ha seguido este enfoque<br />

• Generar <strong>la</strong>s tab<strong>la</strong>s a partir <strong>de</strong> <strong>la</strong> información disponible en<br />

<strong>la</strong>s c<strong>la</strong>ses persistentes<br />

• En <strong>la</strong> mayor parte <strong>de</strong> los casos será preciso especificar<br />

metainformación adicional, como <strong>la</strong> longitud <strong>de</strong> <strong>la</strong>s columnas<br />

<strong>de</strong> tipo ca<strong>de</strong>na <strong>de</strong> caracteres, etc.


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (1)<br />

• En MiniBank se han <strong>de</strong>finido dos c<strong>la</strong>ses persistentes:<br />

• es.udc.pojo.minibank.mo<strong>de</strong>l.account.Account<br />

• es.udc.pojo.minibank.mo<strong>de</strong>l.accountoperation.AccountOperation<br />

• Requisitos <strong>de</strong> una c<strong>la</strong>se persistente<br />

• Se anota <strong>con</strong> @Entity<br />

• NOTA: <strong>la</strong>s anotaciones comunes <strong>con</strong> <strong>la</strong> API <strong>de</strong> persistencia <strong>de</strong><br />

Java están en javax.persistence<br />

• Debe tener un <strong>con</strong>structor sin argumentos público o<br />

protegido (pue<strong>de</strong> tener otros <strong>con</strong>structores)<br />

• No <strong>de</strong>be ser final<br />

• Debe tener una c<strong>la</strong>ve primaria<br />

• Pue<strong>de</strong> exten<strong>de</strong>r <strong>de</strong> otra c<strong>la</strong>se<br />

• Pue<strong>de</strong> ser abstracta<br />

• Pue<strong>de</strong> tener re<strong>la</strong>ciones <strong>de</strong> asociación <strong>con</strong> otras


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (2)<br />

• Requisitos <strong>de</strong> una c<strong>la</strong>se persistente (<strong>con</strong>t)<br />

• Estado persistente<br />

• Si una c<strong>la</strong>se se marca como persistente, por <strong>de</strong>fecto se<br />

incluyen todas sus propieda<strong>de</strong>s como persistentes<br />

• Se pue<strong>de</strong>n excluir propieda<strong>de</strong>s utilizando <strong>la</strong> anotación<br />

@Transient o <strong>de</strong>finiendo <strong>la</strong> propiedad como transient<br />

• Típicamente disponen <strong>de</strong> métodos públicos get y set para<br />

acce<strong>de</strong>r y modificar sus valores<br />

• La implementación <strong>de</strong> <strong>Hibernate</strong> acce<strong>de</strong> al estado directamente<br />

a través <strong>de</strong> sus atributos (tipo <strong>de</strong> acceso: campo) o (exclusivo)<br />

mediante métodos get/set (tipo <strong>de</strong> acceso: propiedad)<br />

• La colocación <strong>de</strong> <strong>la</strong> anotación <strong>de</strong> c<strong>la</strong>ve primaria en los atributos o<br />

(exclusivo) métodos get <strong>de</strong>termina el tipo <strong>de</strong> acceso<br />

• Con el “tipo <strong>de</strong> acceso propiedad”, sólo se pue<strong>de</strong>n colocar<br />

anotaciones en los métodos get


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (3)<br />

• Requisitos <strong>de</strong> una c<strong>la</strong>se persistente (<strong>con</strong>t)<br />

• Estado persistente (<strong>con</strong>t)<br />

• Los atributos o propieda<strong>de</strong>s pue<strong>de</strong>n ser (entre otros)<br />

• Tipos primitivos y sus <strong>con</strong>trapartidas objetuales: se mapean a una<br />

columna<br />

• java.<strong>la</strong>ng.String, java.math.BigInteger,<br />

java.math.BigDecimal, java.util.Date,<br />

java.util.Calendar, java.sql.Date, java.sql.Time,<br />

java.sql.Timestamp, byte[], Byte[], char[] y<br />

Character[]: se mapean a una columna<br />

• Enumerados: se mapean a una columna<br />

• Otras entida<strong>de</strong>s y colecciones <strong>de</strong> entida<strong>de</strong>s: para mapear <strong>la</strong>s<br />

re<strong>la</strong>ciones <strong>con</strong> otras entida<strong>de</strong>s se utilizan columnas y/o tab<strong>la</strong>s<br />

adicionales <strong>de</strong>pendiendo <strong>de</strong> <strong>la</strong> cardinalidad y direccionalidad <strong>de</strong> <strong>la</strong>s<br />

re<strong>la</strong>ciones<br />

• Otros<br />

• Tipos Serializable <strong>de</strong>finidos por el usuario: normalmente se<br />

mapean a un BLOB<br />

• Permite anotar tipos <strong>de</strong>finidos por el usuario para que sus<br />

atributos/propieda<strong>de</strong>s se mapeen a columnas <strong>de</strong> <strong>la</strong> misma tab<strong>la</strong> que<br />

<strong>la</strong> entidad que <strong>la</strong> <strong>con</strong>tiene


@Column(name="accId")<br />

@Column(name="usrId")<br />

@Version<br />

Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (4)<br />

@Entity<br />

Account<br />

@Id<br />

+{get}AccountId() : Long<br />

+{get}UserId() : Long<br />

+{get}Ba<strong>la</strong>nce() : double<br />

+{get}Version() : long<br />

accId usrId ba<strong>la</strong>nce version<br />

Tab<strong>la</strong> = Account<br />

(PK)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (5)<br />

@Entity<br />

public c<strong>la</strong>ss Account {<br />

private Long accountId;<br />

private Long userId;<br />

private double ba<strong>la</strong>nce;<br />

private long version;<br />

public Account() {}<br />

public Account(long userId, double ba<strong>la</strong>nce) {<br />

/**<br />

* NOTE: "accountId" *must* be left as "null" since its value<br />

* is automatically generated.<br />

*/<br />

this.userId = userId;<br />

this.ba<strong>la</strong>nce = ba<strong>la</strong>nce;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (6)<br />

@Column(name="accId")<br />

@SequenceGenerator(<br />

// It only takes effect for<br />

name="AccountIdGenerator", // databases providing i<strong>de</strong>ntifier<br />

sequenceName="AccountSeq") // generators.<br />

@Id<br />

@GeneratedValue(strategy=GenerationType.AUTO,<br />

generator="AccountIdGenerator")<br />

public Long getAccountId() {<br />

return accountId;<br />

}<br />

public void setAccountId(Long accountId) {<br />

this.accountId = accountId;<br />

}<br />

@Column(name="usrId")<br />

public Long getUserId() {<br />

return userId;<br />

}<br />

public void setUserId(Long userId) {<br />

this.userId = userId;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (7)<br />

public double getBa<strong>la</strong>nce() {<br />

return ba<strong>la</strong>nce;<br />

}<br />

public void setBa<strong>la</strong>nce(double ba<strong>la</strong>nce) {<br />

this.ba<strong>la</strong>nce = ba<strong>la</strong>nce;<br />

}<br />

@Version<br />

public long getVersion() {<br />

return version;<br />

}<br />

public void setVersion(long version) {<br />

this.version = version;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (8)<br />

• @Entity<br />

• Especifica que es una c<strong>la</strong>se persistente<br />

• @Column<br />

• Permite especificar información sobre cómo mapear un<br />

atributo/propiedad a una columna <strong>de</strong> <strong>la</strong> BD<br />

• En el ejemplo anterior, <strong>la</strong> propiedad accountId se mapea a <strong>la</strong><br />

columna accId (por <strong>de</strong>fecto se asume que el nombre <strong>de</strong> <strong>la</strong><br />

columna coinci<strong>de</strong> <strong>con</strong> el nombre <strong>de</strong>l atributo/propiedad)<br />

• @Id<br />

• Especifica el atributo/propiedad c<strong>la</strong>ve<br />

• Sólo vale para c<strong>la</strong>ves simples<br />

• <strong>Hibernate</strong> tiene soporte para especificar c<strong>la</strong>ves compuestas,<br />

si fuese necesario<br />

• Normalmente sólo es necesario cuando <strong>la</strong> BD ya estaba creada<br />

y tenía c<strong>la</strong>ves compuestas<br />

• En general lo recomendable es usar siempre c<strong>la</strong>ves simples


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (9)<br />

• Generación automática <strong>de</strong> c<strong>la</strong>ves primarias<br />

• Casi todas <strong>la</strong>s BBDD disponen <strong>de</strong> mecanismos para generar<br />

i<strong>de</strong>ntificadores numéricos automáticamente, que se pue<strong>de</strong>n usar<br />

como c<strong>la</strong>ves<br />

• Algunas disponen <strong>de</strong> <strong>con</strong>tadores (secuencias)<br />

• Se pue<strong>de</strong>n leer e incrementar atómicamente<br />

• e.g.: Oracle, PostgreSQL<br />

• En estas BBDD es posible: (1) generar el i<strong>de</strong>ntificador (mediante una<br />

<strong>con</strong>sulta especial) y (2) a <strong>con</strong>tinuación insertar <strong>la</strong> fi<strong>la</strong> <strong>con</strong> el<br />

i<strong>de</strong>ntificador generado<br />

• Otras BBDD disponen <strong>de</strong> columnas <strong>con</strong>tador<br />

• Cada vez que se inserta una fi<strong>la</strong>, se le asigna automáticamente un valor<br />

a esa columna-<strong>con</strong>tador (el siguiente)<br />

• e.g.: MySQL, PostgreSQL, SQL Server, Sybase<br />

• En estas BBDD es necesario: (1) insertar <strong>la</strong> fi<strong>la</strong> (sin especificar un valor<br />

para <strong>la</strong> columna-<strong>con</strong>tador) y (2) a <strong>con</strong>tinuación leer el valor que se le<br />

ha asignado a <strong>la</strong> columna-<strong>con</strong>tador <strong>la</strong>nzando una <strong>con</strong>sulta especial<br />

(usando <strong>la</strong> API <strong>de</strong> JDBC 3.0, es el propio driver el que se encarga <strong>de</strong><br />

recuperar el valor asignado)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (10)<br />

• Generación automática <strong>de</strong> c<strong>la</strong>ves primarias (<strong>con</strong>t)<br />

• Ejemplo para PostgreSQL – secuencias<br />

• Creación <strong>de</strong> <strong>la</strong> secuencia<br />

CREATE SEQUENCE AccountSeq;<br />

• Creación <strong>de</strong> <strong>la</strong> tab<strong>la</strong><br />

CREATE TABLE Account (<br />

accId BIGINT NOT NULL,<br />

usrId BIGINT NOT NULL,<br />

ba<strong>la</strong>nce DOUBLE PRECISION NOT NULL,<br />

CONSTRAINT AccountPK PRIMARY KEY(accId),<br />

CONSTRAINT validBa<strong>la</strong>nce CHECK ( ba<strong>la</strong>nce >= 0 )<br />

);<br />

• (1) Generar el i<strong>de</strong>ntificador<br />

SELECT nextval('AccountSeq');<br />

-> Devuelve el i<strong>de</strong>ntificador <br />

• (2) Insertar <strong>la</strong> fi<strong>la</strong><br />

INSERT INTO Account(accId, usrId, ba<strong>la</strong>nce)<br />

VALUES (,1,2.0)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (11)<br />

• Generación automática <strong>de</strong> c<strong>la</strong>ves primarias (<strong>con</strong>t)<br />

• Ejemplo para MySQL – columnas-<strong>con</strong>tador<br />

• Creación <strong>de</strong> <strong>la</strong> tab<strong>la</strong><br />

CREATE TABLE Account (<br />

accId BIGINT NOT NULL AUTO_INCREMENT,<br />

usrId BIGINT NOT NULL,<br />

ba<strong>la</strong>nce DOUBLE PRECISION NOT NULL,<br />

CONSTRAINT AccountPK PRIMARY KEY(accId),<br />

CONSTRAINT validBa<strong>la</strong>nce CHECK ( ba<strong>la</strong>nce >= 0 )<br />

) ENGINE = InnoDB;<br />

• En MySQL, ENGINE = InnoDB es necesario para permitir y tratar<br />

a<strong>de</strong>cuadamente transacciones y restricciones <strong>de</strong> integridad referencial<br />

• (1) Insertar <strong>la</strong> fi<strong>la</strong><br />

INSERT INTO Account(usrId, ba<strong>la</strong>nce)<br />

VALUES (1,2.0)<br />

• (2) Leer el valor que se <strong>la</strong> ha asignado a <strong>la</strong> columna-<strong>con</strong>tador<br />

• Ejecutar <strong>la</strong> siguiente <strong>con</strong>sulta para obtener el i<strong>de</strong>ntificador<br />

SELECT LAST_INSERT_ID();<br />

• O usar <strong>la</strong> API <strong>de</strong> JDBC 3.0 que proporciona un método para recuperar el<br />

valor <strong>de</strong>l i<strong>de</strong>ntificador


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (12)<br />

• En Account se han usado anotaciones especiales en <strong>la</strong><br />

propiedad <strong>de</strong> <strong>la</strong> c<strong>la</strong>ve primaria para que<br />

• Los valores se generen mediante una secuencia (si <strong>la</strong> BD<br />

proporciona secuencias)<br />

• Se mapee a una columna <strong>con</strong>tador (si <strong>la</strong> BD ofrece columnas<br />

<strong>con</strong>tador)<br />

• @GeneratedValue<br />

• strategy<br />

• Especifica <strong>la</strong> estrategia <strong>de</strong> generación <strong>de</strong> i<strong>de</strong>ntificadores numéricos<br />

• Es <strong>de</strong> tipo GenerationType (enumerado). Entre otros, es posible<br />

especificar los siguientes valores<br />

• SEQUENCE: usar una secuencia<br />

• generator<br />

• IDENTITY: mapear <strong>la</strong> c<strong>la</strong>ve a una columna <strong>con</strong>tador<br />

• AUTO: <strong>Hibernate</strong> <strong>de</strong>ci<strong>de</strong> <strong>la</strong> estrategia en función <strong>de</strong> <strong>la</strong> BD usada<br />

• Especifica el nombre <strong>de</strong>l generador que se usará en el caso <strong>de</strong> <strong>la</strong><br />

estrategia SEQUENCE


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (13)<br />

• @SequenceGenerator<br />

• Especifica información <strong>de</strong> mapping para un generador que<br />

se pue<strong>de</strong> usar <strong>con</strong> <strong>la</strong> estrategia SEQUENCE<br />

• name: nombre <strong>de</strong>l generador<br />

• sequenceName: nombre <strong>de</strong> <strong>la</strong> secuencia<br />

• Los atributos/propieda<strong>de</strong>s anotadas tienen que ser<br />

<strong>de</strong> tipo entero (short, int, long o sus<br />

<strong>con</strong>trapartidas objetuales)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (14)<br />

• ¿Cómo especificar que utilice secuencias o columnas <strong>con</strong>tador<br />

para <strong>la</strong> propiedad accountId?<br />

• En el ejemplo se ha especificado<br />

@GeneratedValue(strategy=GenerationType.AUTO,<br />

generator="AccountIdGenerator")<br />

• <strong>Hibernate</strong> utiliza <strong>la</strong> estrategia más apropiada a <strong>la</strong> BD usada<br />

• Por ejemplo, para MySQL utiliza IDENTITY, y para PostgreSQL utiliza<br />

SEQUENCE (y el generador especificado)<br />

• Esto evita tener que especificar explícitamente (en función <strong>de</strong> <strong>la</strong> BD)<br />

@GeneratedValue(strategy=GenerationType.IDENTITY)<br />

o<br />

@GeneratedValue(strategy=GenerationType.SEQUENCE,<br />

generator="AccountIdGenerator")<br />

• El <strong>con</strong>structor <strong>con</strong> argumentos <strong>de</strong>ja el atributo accountId sin<br />

inicializar<br />

• La implementación <strong>de</strong> los casos <strong>de</strong> uso invoca este <strong>con</strong>structor<br />

cuando tiene que crear una instancia <strong>de</strong> Account<br />

• No pue<strong>de</strong> dar valor al atributo accountId: es <strong>Hibernate</strong> el que le<br />

asignará valor tras hacer el objeto persistente (invocando el<br />

método setAccountId)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (15)<br />

• <strong>Hibernate</strong> asume que por <strong>de</strong>fecto no se utiliza un<br />

nivel <strong>de</strong> ais<strong>la</strong>miento en sus transacciones mayor que<br />

TRANSACTION_READ_COMMITED<br />

• No se evitan “non-repeatable reads” y “phantom reads”<br />

• Problema si <strong>la</strong>s transacciones releen datos <strong>de</strong> <strong>la</strong> BD<br />

• E.g.: Realización <strong>de</strong> dos ingresos <strong>con</strong>currentes sobre<br />

<strong>la</strong> misma cuenta <strong>de</strong> un usuario (no <strong>con</strong>si<strong>de</strong>ramos<br />

operaciones bancarias para simplificar)<br />

• Transacción 1: aña<strong>de</strong> 40€ a <strong>la</strong> cuenta 1 <strong>de</strong>l usuario 1<br />

• Transacción 2: aña<strong>de</strong> 20€ a <strong>la</strong> cuenta 1 <strong>de</strong>l usuario 1


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (16)<br />

Transacción 1 Transacción 2 Estado en BD<br />

begin<br />

SELECT ba<strong>la</strong>nce<br />

/* ba<strong>la</strong>nce = 100 */<br />

/* Calcu<strong>la</strong>r nuevo ba<strong>la</strong>nce en<br />

memoria, ba<strong>la</strong>nce = 140 */<br />

UPDATE ba<strong>la</strong>nce<br />

/* ba<strong>la</strong>nce = 140 en BD pero<br />

no comprometido */<br />

commit<br />

/* ba<strong>la</strong>nce = 140 en BD y<br />

comprometido */<br />

begin<br />

SELECT ba<strong>la</strong>nce<br />

/* ba<strong>la</strong>nce = 100 */<br />

/* Calcu<strong>la</strong>r nuevo ba<strong>la</strong>nce en<br />

memoria, ba<strong>la</strong>nce = 120 */<br />

UPDATE ba<strong>la</strong>nce<br />

/* ba<strong>la</strong>nce = 120 en BD pero<br />

no comprometido */<br />

commit<br />

/* ba<strong>la</strong>nce = 120 en BD y<br />

comprometido */<br />

accId usrId ba<strong>la</strong>nce<br />

1 1 100<br />

1 1 100<br />

1 1 100<br />

1 1 140<br />

1 1 140<br />

1 1 120


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (17)<br />

• Problema: hemos perdido los efectos <strong>de</strong> <strong>la</strong> primera<br />

<strong>de</strong> <strong>la</strong>s transacciones<br />

• Este problema es <strong>con</strong>ocido <strong>con</strong> el nombre <strong>de</strong> se<strong>con</strong>d<br />

lost update<br />

• Es un caso particu<strong>la</strong>r <strong>de</strong>l problema "non-repeatable reads”<br />

• Es posible resolverlo sin recurrir a<br />

TRANSACTION_REPEATABLE_READ (o superior)<br />

usando TRANSACTION_READ_COMMITED y <strong>la</strong><br />

estrategia Optimistic Locking<br />

• En general, es <strong>la</strong> estrategia recomendada por <strong>Hibernate</strong>, dado que<br />

maximiza <strong>la</strong> eficiencia (menor nivel <strong>de</strong> ais<strong>la</strong>miento => menos bloqueos en<br />

<strong>la</strong> BD) y es a<strong>de</strong>cuada para <strong>la</strong> mayor parte <strong>de</strong> transacciones<br />

• Las transacciones que <strong>la</strong>nzan más <strong>de</strong> una vez una misma <strong>con</strong>sulta, tienen<br />

que emplear un nivel <strong>de</strong> ais<strong>la</strong>miento mayor


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (18)<br />

• Optimistic Locking<br />

• <strong>Hibernate</strong> permite utilizar Optimistic Locking en <strong>la</strong><br />

ejecución <strong>de</strong> transacciones para resolver este<br />

problema<br />

• Se aña<strong>de</strong> un atributo/propiedad version en <strong>la</strong>s<br />

entida<strong>de</strong>s modificables, anotándo<strong>la</strong>s <strong>con</strong> @Version<br />

• Cada vez que se actualiza una instancia <strong>de</strong> una entidad,<br />

<strong>Hibernate</strong> comprueba si el atributo version coinci<strong>de</strong><br />

<strong>con</strong> su valor actual<br />

• En caso afirmativo, realiza <strong>la</strong> actualización y lo incrementa<br />

en una unidad<br />

• En otro caso, <strong>la</strong>nza una excepción <strong>de</strong> Runtime<br />

(org.hibernate.StaleObjectStateException o<br />

org.hibernate.StaleStateException)


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (19)<br />

• Optimistic Locking (<strong>con</strong>t)<br />

• El <strong>de</strong>sarrol<strong>la</strong>dor no <strong>de</strong>be modificar el valor <strong>de</strong> este<br />

atributo/propiedad<br />

• Tipos <strong>de</strong> atributos/propieda<strong>de</strong>s a <strong>la</strong>s que es aplicable: short, int,<br />

long y sus <strong>con</strong>trapartidas objetuales, y java.sql.Timestamp<br />

• <strong>Hibernate</strong> implementa una actualización <strong>de</strong> <strong>la</strong> siguiente forma<br />

UPDATE Account<br />

SET usrId = ?, ba<strong>la</strong>nce = ?, version = version + 1<br />

WHERE accId = ? AND version = ?<br />

• Si <strong>de</strong>vuelve 1 => se actualizó <strong>la</strong> fi<strong>la</strong> (y en <strong>con</strong>secuencia, no hay<br />

<strong>con</strong>flictos <strong>con</strong> otra transacción)<br />

• version se incrementa en 1 (version = version + 1) en <strong>la</strong> fi<strong>la</strong> y en<br />

<strong>la</strong> instancia <strong>de</strong> Account en memoria<br />

• Si <strong>de</strong>vuelve 0 => No se actualizó <strong>la</strong> fi<strong>la</strong> (otra transacción actualizó<br />

antes esa fi<strong>la</strong>)<br />

• El valor <strong>de</strong> <strong>la</strong> columna version era superior al que tenía <strong>la</strong> instancia <strong>de</strong><br />

Account en memoria<br />

• Se produciría <strong>la</strong> excepción <strong>de</strong> Runtime


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (20)<br />

Transacción 1 Transacción 2 Estado en BD<br />

begin<br />

accId usrId<br />

ba<strong>la</strong>nce<br />

version<br />

SELECT ba<strong>la</strong>nce, version<br />

/* ba<strong>la</strong>nce = 100, version = 1 */<br />

1<br />

1 100<br />

1<br />

/* Calcu<strong>la</strong>r nuevo ba<strong>la</strong>nce en<br />

memoria, ba<strong>la</strong>nce = 140 */<br />

begin<br />

1 1 100<br />

1<br />

UPDATE ba<strong>la</strong>nce, version<br />

WHERE version = 1<br />

/* ba<strong>la</strong>nce = 140, version = 2<br />

en BD pero no comprometido */<br />

SELECT ba<strong>la</strong>nce, version<br />

/* ba<strong>la</strong>nce = 100, version = 1 */<br />

1 1 100<br />

1<br />

commit<br />

/* ba<strong>la</strong>nce = 140, version =2 en<br />

BD y comprometido */<br />

/* Calcu<strong>la</strong>r nuevo ba<strong>la</strong>nce en<br />

memoria, ba<strong>la</strong>nce = 120 */<br />

UPDATE ba<strong>la</strong>nce, version<br />

WHERE version = 1<br />

-><br />

StaleObjectStateException<br />

1 1 140<br />

2<br />

1 1 140 2


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (21)<br />

• Optimistic Locking (<strong>con</strong>t)<br />

• La estrategia es optimista porque<br />

• No realiza un bloqueo especial sobre <strong>la</strong>s fi<strong>la</strong>s que se leen<br />

durante <strong>la</strong> transacción<br />

• Retrasa al final <strong>de</strong> <strong>la</strong> transacción <strong>la</strong> comprobación <strong>de</strong> si<br />

hubo <strong>con</strong>flictos <strong>con</strong> otras transacciones<br />

• La estrategia Optimistic Locking también es útil<br />

para implementar <strong>con</strong>versaciones<br />

• Las <strong>con</strong>versaciones permiten englobar en una única<br />

transacción <strong>de</strong> 'usuario' varios casos <strong>de</strong> uso invocados<br />

por un mismo usuario para realizar una tarea <strong>con</strong>creta<br />

(más información en "Java Persistence with <strong>Hibernate</strong>")


@Column(name="accOpId")<br />

@ManyToOne(optional=false,<br />

fetch=FetchType.LAZY)<br />

@JoinColumn(name="accId")<br />

@Temporal(TemporalType.TIMESTAMP)<br />

@Table(name="AccountOp")<br />

Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (22)<br />

@Entity<br />

AccountOperation<br />

@Id<br />

+{get}AccountOperationId() : Long<br />

+{get}Account() : Account<br />

+{get}Date() : Calendar<br />

+{get}Type() : Type<br />

+{get}Amount() : double<br />

0..n<br />

1<br />

Account<br />

(PK)<br />

(FK)<br />

accOpId accId date type amount<br />

Tab<strong>la</strong> = AccountOp<br />

(PK)<br />

accId usrId ba<strong>la</strong>nce version<br />

Tab<strong>la</strong> = Account


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (23)<br />

@Entity<br />

@org.hibernate.annotations.Entity(mutable=false)<br />

@Table(name="AccountOp")<br />

public c<strong>la</strong>ss AccountOperation {<br />

public enum Type {ADD, WITHDRAW};<br />

private Long accountOperationId;<br />

private Account account;<br />

private Calendar date;<br />

private Type type;<br />

private double amount;<br />

public AccountOperation() {}<br />

public AccountOperation(Account account, Calendar date,<br />

Type type, double amount) {<br />

/**<br />

* NOTE: "accountOperationId" *must* be left as "null" since<br />

* its value is automatically generated.<br />

*/<br />

this.account = account;<br />

this.date = date;<br />

this.type = type;<br />

this.amount = amount;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (24)<br />

@Column(name="accOpId")<br />

@SequenceGenerator(<br />

// It only takes effect<br />

name="AccountOperationIdGenerator", // for databases provi-<br />

// ding intentifier<br />

sequenceName="AccountOpSeq") // generators.<br />

@Id<br />

@GeneratedValue(strategy=GenerationType.AUTO,<br />

generator="AccountOperationIdGenerator")<br />

public Long getAccountOperationId() {<br />

return accountOperationId;<br />

}<br />

public void setAccountOperationId (Long accountOperationId) {<br />

this.accountOperationId = accountOperationId;<br />

}<br />

@ManyToOne(optional=false, fetch=FetchType.LAZY)<br />

@JoinColumn(name="accId")<br />

public Account getAccount() {<br />

return account;<br />

}<br />

public void setAccount(Account account) {<br />

this.account = account;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (25)<br />

@Temporal(TemporalType.TIMESTAMP)<br />

public Calendar getDate() {<br />

return date;<br />

}<br />

public void setDate(Calendar date) {<br />

this.date = date;<br />

}<br />

public Type getType() {<br />

return type;<br />

}<br />

public void setType(Type type) {<br />

this.type = type;<br />

}<br />

public double getAmount() {<br />

return amount;<br />

}<br />

}<br />

public void setAmount(double amount) {<br />

this.amount = amount;<br />

}


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (26)<br />

• Utiliza anotaciones especiales para <strong>la</strong> generación <strong>de</strong>l<br />

i<strong>de</strong>ntificador numérico para <strong>la</strong> c<strong>la</strong>ve primaria, como Account<br />

• AccountOperation es una entidad que nunca es modificada<br />

• @org.hibernate.annotations.Entity(mutable=false)<br />

• Esta anotación permite especificar esta situación a <strong>Hibernate</strong>, para<br />

aplicar optimizaciones <strong>de</strong> tiempo <strong>de</strong> ejecución<br />

• Por tanto, no <strong>de</strong>fine <strong>la</strong> propiedad (ni <strong>la</strong> anotación) version<br />

• @Table<br />

• Por <strong>de</strong>fecto, cada entidad se mapea a una tab<strong>la</strong> <strong>con</strong> el mismo<br />

nombre que <strong>la</strong> entidad<br />

• @Table permite especificar un nombre particu<strong>la</strong>r para <strong>la</strong> tab<strong>la</strong>, en<br />

este caso AccountOp<br />

• @Temporal<br />

• Para los atributos/propieda<strong>de</strong>s <strong>de</strong> tipo Calendar hay que<br />

especificar el mapping que se <strong>de</strong>sea<br />

• TemporalType.DATE, TemporalType.TIME o<br />

TemporalType.TIMESTAMP


Anotación <strong>de</strong> C<strong>la</strong>ses Persistentes (y 27)<br />

• Las enumeraciones (en este caso Type) se<br />

almacenan en BD como String o como un ordinal,<br />

<strong>de</strong>pendiendo <strong>de</strong>l tipo <strong>de</strong> <strong>la</strong> columna <strong>de</strong> <strong>la</strong> BD <strong>con</strong> <strong>la</strong><br />

que se mapea<br />

• Se pue<strong>de</strong> utilizar <strong>la</strong> anotación @Enumerated para forzar<br />

una <strong>de</strong> <strong>la</strong>s estrategias:<br />

• @Enumerated(EnumType.ORDINAL)<br />

• Utiliza <strong>la</strong> posición <strong>de</strong> <strong>la</strong> opción en <strong>la</strong> enumeración<br />

• @Enumerated(EnumType.STRING)<br />

• Utiliza el nombre <strong>de</strong> <strong>la</strong> <strong>con</strong>stante en <strong>la</strong> enumeración<br />

• En nuestro caso, como se mapea <strong>con</strong> una columna<br />

numérica en BD, se utiliza <strong>la</strong> estrategia ORDINAL


Anotando Re<strong>la</strong>ciones (1)<br />

• Tipos <strong>de</strong> re<strong>la</strong>ciones<br />

• Uno-a-Uno, Uno-a-Muchos/Muchos-a-Uno, Muchos-a-Muchos<br />

• Uno significa 0..1 o 1; Muchos significa 0..N o N<br />

• Atributos/propieda<strong>de</strong>s que representan re<strong>la</strong>ciones<br />

• Tipos: c<strong>la</strong>ses persistentes o colecciones <strong>de</strong> c<strong>la</strong>ses persistentes (utilizando<br />

<strong>la</strong>s interfaces Collection, List, Set y Map)<br />

• Se utilizan anotaciones para especificar cómo mapear <strong>la</strong>s re<strong>la</strong>ciones a<br />

columnas/tab<strong>la</strong>s<br />

• Direccionalidad<br />

• Unidireccionales o Bidireccionales<br />

• Lado propietario (“owning si<strong>de</strong>”) y <strong>la</strong>do inverso (“inverse si<strong>de</strong>”) en<br />

una re<strong>la</strong>ción<br />

• El <strong>la</strong>do propietario es <strong>la</strong> entidad cuya tab<strong>la</strong> asociada tiene <strong>la</strong> c<strong>la</strong>ve foránea<br />

que mantiene <strong>la</strong> re<strong>la</strong>ción<br />

• Una re<strong>la</strong>ción unidireccional sólo tiene <strong>la</strong>do propietario<br />

• El que permite navegar hacia <strong>la</strong> otra entidad<br />

• En una re<strong>la</strong>ción bidireccional<br />

• Si es Uno-a-Muchos o Muchos-a-Uno, el <strong>la</strong>do propietario es el <strong>la</strong>do Muchos<br />

• Si es Uno-a-Uno, <strong>la</strong> entidad cuya tab<strong>la</strong> <strong>con</strong>tiene <strong>la</strong> c<strong>la</strong>ve foránea es el <strong>la</strong>do<br />

propietario<br />

• Si es Muchos-a-Muchos, cualquier <strong>la</strong>do pue<strong>de</strong> ser el <strong>la</strong>do propietario


Anotando Re<strong>la</strong>ciones – 1:N (2)<br />

• Entre Account y AccountOperation existe una re<strong>la</strong>ción<br />

unidireccional, Muchos-a-Uno, que es necesario mapear a BD<br />

• Des<strong>de</strong> AccountOperation se pue<strong>de</strong> recuperar el objeto<br />

Account al que pertenece <strong>la</strong> operación<br />

• En cambio <strong>de</strong>s<strong>de</strong> Account no se pue<strong>de</strong>n recuperar los objetos<br />

AccountOperation asociados<br />

Account (1) AccountOperation<br />

porque es posible que una cuenta tenga muchas operaciones, y no<br />

es factible recuperar<strong>la</strong>s todas juntas<br />

• Para recuperar <strong>la</strong>s operaciones <strong>de</strong> una cuenta se utiliza el patrón<br />

page by page iterator


Anotando Re<strong>la</strong>ciones – 1:N (3)<br />

• Re<strong>la</strong>ciones Uno-a-Muchos / Muchos-a-Uno<br />

• Se utiliza @OneToMany (<strong>la</strong>do Uno) o @ManyToOne (<strong>la</strong>do<br />

Muchos) sobre los atributos/propieda<strong>de</strong>s que <strong>de</strong>finen <strong>la</strong><br />

re<strong>la</strong>ción<br />

• Si <strong>la</strong> re<strong>la</strong>ción es unidireccional, sólo se anota el <strong>la</strong>do que<br />

permite navegar hacia el otro<br />

• En el caso Account


Anotando Re<strong>la</strong>ciones – 1:N (4)<br />

• Re<strong>la</strong>ciones Uno-a-Muchos / Muchos-a-Uno<br />

• Si <strong>la</strong> re<strong>la</strong>ción tratada fuese bidireccional<br />

Account (1) AccountOperation (0..N)<br />

en Account habría que añadir<br />

public c<strong>la</strong>ss Account {<br />

// ...<br />

private Set accountOperations;<br />

@OneToMany(mappedBy = "account")<br />

public Set getAccountOperations() {<br />

return accountOperations;<br />

}<br />

}<br />

public void setAccountOperations(<br />

Set accountOperations) {<br />

this.accountOperations = accountOperations;<br />

}<br />

// ...


Anotando Re<strong>la</strong>ciones – 1:N (5)<br />

• En <strong>la</strong>s re<strong>la</strong>ciones bidireccionales, el <strong>la</strong>do inverso tiene<br />

que usar el elemento mappedBy en @OneToOne,<br />

@OneToMany y @ManyToMany<br />

• No se pue<strong>de</strong> usar en @ManyToOne porque en una re<strong>la</strong>ción<br />

bidireccional el <strong>la</strong>do Muchos siempre es el <strong>la</strong>do propietario<br />

• mappedBy especifica el nombre <strong>de</strong>l atributo/propiedad <strong>de</strong>l<br />

otro <strong>la</strong>do (<strong>la</strong>do propietario) <strong>de</strong> <strong>la</strong> re<strong>la</strong>ción<br />

• En el ejemplo, el elemento mappedBy está diciendo que <strong>la</strong><br />

propiedad account <strong>de</strong> AccountOperation<br />

<strong>con</strong>juntamente <strong>con</strong> <strong>la</strong> propiedad accountOperations <strong>de</strong><br />

Account forman una re<strong>la</strong>ción bidireccional


Anotando Re<strong>la</strong>ciones – 1:1 (6)<br />

• C<strong>la</strong>ses Persistentes (ejemplo completo apartado 3.6)<br />

Employee<br />

- employeeId : Long<br />

- firstName : String<br />

- <strong>la</strong>stName : String<br />

- positionId : String<br />

- sa<strong>la</strong>ry : int<br />

Dirigido por<br />

1 0..1<br />

Department<br />

- <strong>de</strong>partmentId : Long<br />

- name : String<br />

- creationDate : Calendar<br />

• Tab<strong>la</strong>s<br />

(PK)<br />

(FK)<br />

<strong>de</strong>pId dirId name creationDate version Tab<strong>la</strong> = Department<br />

Re<strong>la</strong>ción “Department(0..1)--[Dirigido por]-->Employee(1)”<br />

(PK)<br />

(FK)<br />

empId <strong>de</strong>pId firstName <strong>la</strong>stName posId sa<strong>la</strong>ry version type Tab<strong>la</strong> = Employee


Anotando Re<strong>la</strong>ciones – 1:1 (7)<br />

• Department(0..1)--[Dirigido por]--> Employee(1)<br />

• En Department<br />

@Entity<br />

public c<strong>la</strong>ss Department {<br />

}<br />

// ...<br />

@OneToOne(fetch=FetchType.LAZY)<br />

@JoinColumn(name="dirId")<br />

public Employee getDirector() {<br />

return director;<br />

}<br />

public void setDirector(Employee director) {<br />

this.director = director;<br />

}<br />

// ...


Anotando Re<strong>la</strong>ciones – 1:1 (y 8)<br />

• Re<strong>la</strong>ciones Uno-a-Uno<br />

• Se utiliza @OneToOne sobre los atributos/propieda<strong>de</strong>s que<br />

<strong>de</strong>finen <strong>la</strong> re<strong>la</strong>ción<br />

• En el ejemplo, dado que <strong>la</strong> re<strong>la</strong>ción es unidireccional, sólo se<br />

aplica sobre el método getDirector <strong>de</strong> <strong>la</strong> entidad<br />

Department (en otro caso, se aplicaría en ambas entida<strong>de</strong>s)<br />

• Se utiliza @JoinColumn sobre el atributo/propiedad que<br />

<strong>de</strong>fine <strong>la</strong> re<strong>la</strong>ción en el <strong>la</strong>do propietario<br />

• En el ejemplo, dado que <strong>la</strong> re<strong>la</strong>ción es unidireccional, el <strong>la</strong>do<br />

propietario es Department<br />

• Especifica que <strong>la</strong> columna dirId actúa como c<strong>la</strong>ve foránea<br />

para mantener <strong>la</strong> re<strong>la</strong>ción<br />

• Se pue<strong>de</strong> usar el elemento referencedColumnName para<br />

especificar el nombre <strong>de</strong> <strong>la</strong> columna a <strong>la</strong> que hace referencia <strong>la</strong><br />

c<strong>la</strong>ve foránea<br />

• Por <strong>de</strong>fecto se asume que es <strong>la</strong> c<strong>la</strong>ve primaria <strong>de</strong> <strong>la</strong> tab<strong>la</strong> <strong>de</strong> <strong>la</strong><br />

otra entidad


Gestión <strong>de</strong> Re<strong>la</strong>ciones (1)<br />

• Cuando una entidad tiene una re<strong>la</strong>ción <strong>con</strong> una o<br />

varias entida<strong>de</strong>s, es importante por temas <strong>de</strong><br />

eficiencia <strong>con</strong>ocer cómo gestiona <strong>Hibernate</strong> <strong>la</strong><br />

recuperación <strong>de</strong> <strong>la</strong>s instancias re<strong>la</strong>cionadas<br />

• <strong>Hibernate</strong> por <strong>de</strong>fecto utiliza una estrategia LAZY<br />

para re<strong>la</strong>ciones, excepto en los casos Muchos-a-Uno<br />

y Uno-a-Uno (que utiliza una estrategia EAGER por<br />

compatibilidad <strong>con</strong> JPA)<br />

• LAZY – Ap<strong>la</strong>za <strong>la</strong> recuperación <strong>de</strong> <strong>la</strong> entidad hasta que es<br />

necesaria<br />

• EAGER – Cuando se recupera un objeto persistente, se<br />

recuperan también todos los objetos persistentes que<br />

presenten este tipo <strong>de</strong> asociación


Gestión <strong>de</strong> Re<strong>la</strong>ciones (2)<br />

• ¿Qué ocurre cuando se invoca al método get que<br />

permite atravesar una re<strong>la</strong>ción LAZY?<br />

• Si <strong>la</strong> re<strong>la</strong>ción es Uno/Muchos-a-Uno<br />

• <strong>Hibernate</strong> <strong>de</strong>vuelve un proxy <strong>de</strong>l objeto re<strong>la</strong>cionado que<br />

<strong>con</strong>tiene <strong>la</strong> c<strong>la</strong>ve primaria <strong>de</strong> <strong>la</strong> entidad<br />

• Los proxies los genera <strong>Hibernate</strong> dinámicamente en tiempo <strong>de</strong><br />

ejecución y son c<strong>la</strong>ses que extien<strong>de</strong>n a <strong>la</strong> c<strong>la</strong>se original<br />

• En el caso <strong>de</strong> un proxy <strong>de</strong> una entidad que here<strong>de</strong> <strong>de</strong> otra, el<br />

proxy extien<strong>de</strong> a <strong>la</strong> c<strong>la</strong>se raíz<br />

• Si se invoca cualquier método sobre el proxy, excepto<br />

get, <strong>Hibernate</strong> inicializa el proxy (es <strong>de</strong>cir,<br />

recupera el estado <strong>de</strong> BD)<br />

• Si <strong>la</strong> re<strong>la</strong>ción es Uno/Muchos-a-Muchos<br />

• <strong>Hibernate</strong> <strong>de</strong>vuelve una colección que implementa <strong>la</strong> interfaz<br />

usada (List, Set, etc.) para mo<strong>de</strong><strong>la</strong>r <strong>la</strong> re<strong>la</strong>ción<br />

• La colección se inicializa (es <strong>de</strong>cir, recupera <strong>la</strong>s instancias<br />

re<strong>la</strong>cionadas <strong>de</strong> BD) cuando se invoca alguna operación <strong>de</strong> <strong>la</strong><br />

colección (size, <strong>con</strong>tains, iterar, etc.)


Gestión <strong>de</strong> Re<strong>la</strong>ciones (y 3)<br />

• E.g. Muchos-a-Uno: (Account


Gestión <strong>de</strong> <strong>la</strong> <strong>Persistencia</strong> (1)<br />

• org.hibernate.Session<br />

• Constituye <strong>la</strong> principal abstracción <strong>de</strong> <strong>la</strong> API <strong>de</strong> <strong>Hibernate</strong><br />

• Incluye soporte para <strong>la</strong>s operaciones CRUD básicas sobre<br />

entida<strong>de</strong>s<br />

• Permite <strong>con</strong>struir búsquedas complejas mediante el objeto<br />

Query<br />

• Permite gestionar transacciones mediante el objeto<br />

Transaction<br />

• Representa un gateway <strong>con</strong>tra <strong>la</strong> BD<br />

• Mantiene internamente una <strong>con</strong>exión <strong>con</strong>tra <strong>la</strong> BD<br />

• Contiene un mapa <strong>con</strong> <strong>la</strong>s entida<strong>de</strong>s que<br />

obtiene/almacena/modifica y funciona a modo <strong>de</strong> caché <strong>de</strong><br />

objetos persistentes<br />

• Sincroniza <strong>de</strong> forma automática el estado <strong>de</strong> <strong>la</strong>s entida<strong>de</strong>s<br />

persistentes <strong>con</strong> el <strong>de</strong> <strong>la</strong> BD<br />

• Intenta retrasar <strong>la</strong> sincronización todo lo posible


Gestión <strong>de</strong> <strong>la</strong> <strong>Persistencia</strong> (y 2)<br />

• Operaciones CRUD<br />

• Insertar un objeto persistente o actualizar uno existente<br />

• saveOrUpdate(Object entity)<br />

• Eliminar un objeto persistente<br />

• <strong>de</strong>lete(Object entity)<br />

• Recuperar un objeto persistente a partir <strong>de</strong> su c<strong>la</strong>se y su<br />

c<strong>la</strong>ve primaria<br />

• get(C<strong>la</strong>ss entityC<strong>la</strong>ss, Serializable primaryKey)<br />

• Devuelve null si <strong>la</strong> instancia no existe<br />

• saveOrUpdate y get asocian <strong>la</strong> instancia<br />

persistente <strong>con</strong> el objeto Session<br />

• <strong>de</strong>lete elimina <strong>la</strong> asociación <strong>de</strong> <strong>la</strong> instancia (que<br />

<strong>de</strong>ja <strong>de</strong> ser persistente) <strong>con</strong> el objeto Session


Transacción Típica <strong>con</strong> <strong>Hibernate</strong> (1)<br />

• El objeto Session se obtiene a partir <strong>de</strong> un objeto SessionFactory,<br />

invocando el método openSession<br />

• Un objeto SessionFactory representa una <strong>con</strong>figuración particu<strong>la</strong>r <strong>de</strong> un<br />

<strong>con</strong>junto <strong>de</strong> metadatos <strong>de</strong> mapping objeto/re<strong>la</strong>cional<br />

Session session = <strong>Hibernate</strong>Util.getSessionFactory().openSession();<br />

Transaction tx = null;<br />

try {<br />

tx = session.beginTransaction();<br />

// Utilizar <strong>la</strong> Session para saveOrUpdate/get/<strong>de</strong>lete/...<br />

tx.commit();<br />

} catch (Exception e) {<br />

if (tx != null) {<br />

tx.rollback();<br />

throw e;<br />

}<br />

} finally {<br />

session.close();<br />

}<br />

// Al finalizar <strong>la</strong> aplicación ...<br />

<strong>Hibernate</strong>Util.shutdown();


Transacción Típica <strong>con</strong> <strong>Hibernate</strong> (y 2)<br />

• Cuando se crea el objeto Session, se le asigna <strong>la</strong> <strong>con</strong>exión <strong>de</strong><br />

BD que va a utilizar<br />

• Una vez obtenido el objeto Session, se crea una nueva unidad<br />

<strong>de</strong> trabajo (Transaction) utilizando el método<br />

beginTransaction<br />

• Dentro <strong>de</strong>l <strong>con</strong>texto <strong>de</strong> <strong>la</strong> transacción creada, se pue<strong>de</strong>n invocar<br />

los métodos <strong>de</strong> gestión <strong>de</strong> persistencia proporcionados por el<br />

objeto Session, para recuperar, añadir, eliminar o modificar el<br />

estado <strong>de</strong> instancias <strong>de</strong> c<strong>la</strong>ses persistentes. También se pue<strong>de</strong>n<br />

realizar <strong>con</strong>sultas<br />

• Si <strong>la</strong>s operaciones <strong>de</strong> persistencia no han producido ninguna<br />

excepción, se invoca el método commit <strong>de</strong> <strong>la</strong> unidad <strong>de</strong> trabajo<br />

para <strong>con</strong>firmar los cambios realizados. En caso <strong>con</strong>trario, se<br />

realiza un rollback para <strong>de</strong>shacer los cambios producidos<br />

• Sobre un mismo objeto Session pue<strong>de</strong>n crearse varias<br />

unida<strong>de</strong>s <strong>de</strong> trabajo<br />

• Finalmente se cierra el objeto Session invocando su método<br />

close


Ejemplos <strong>de</strong> Gestión <strong>de</strong> <strong>Persistencia</strong><br />

• Los siguientes ejemplos utilizan <strong>la</strong> p<strong>la</strong>ntil<strong>la</strong> <strong>de</strong>finida en <strong>la</strong><br />

transparencia anterior<br />

• es.udc.pojo.minibank.test.experiments.hibernate.*<br />

• CreateAccount<br />

• Crea una nueva cuenta para el usuario <strong>con</strong> i<strong>de</strong>ntificador 1 y ba<strong>la</strong>nce<br />

inicial 200<br />

Account account = new Account(1, 200);<br />

session.saveOrUpdate(account);<br />

• FindAccount <br />

• Encuentra una cuenta a partir <strong>de</strong> su i<strong>de</strong>ntificador, que recibe como<br />

parámetro<br />

Account account =<br />

(Account) session.get(Account.c<strong>la</strong>ss, accountId);<br />

• RemoveAccount <br />

• Elimina una cuenta a partir <strong>de</strong> su i<strong>de</strong>ntificador, que recibe como<br />

parámetro<br />

Account account = (Account)<br />

session.get(Account.c<strong>la</strong>ss, accountId);<br />

if (account != null) {<br />

session.<strong>de</strong>lete(account)<br />

}


Obtención <strong>de</strong> <strong>la</strong> SessionFactory (1)<br />

public c<strong>la</strong>ss <strong>Hibernate</strong>Util {<br />

}<br />

private static final SessionFactory sessionFactory;<br />

static {<br />

try {<br />

sessionFactory = new AnnotationConfiguration().<br />

<strong>con</strong>figure("hibernate-tutorial-hibernate-<strong>con</strong>fig-test.xml").<br />

buildSessionFactory();<br />

} catch (Throwable ex) {<br />

throw new ExceptionInInitializerError(ex);<br />

}<br />

}<br />

public static SessionFactory getSessionFactory() {<br />

return sessionFactory;<br />

}<br />

public static void shutdown() {<br />

getSessionFactory().close();<br />

}


Obtención <strong>de</strong> <strong>la</strong> SessionFactory (y 2)<br />

• La forma habitual <strong>de</strong> inicializar <strong>Hibernate</strong> es creando un objeto<br />

SessionFactory a partir <strong>de</strong> un objeto<br />

AnnotationConfiguration<br />

• Un objeto AnnotationConfiguration es una representación<br />

objetual <strong>de</strong>l fichero <strong>de</strong> <strong>con</strong>figuración <strong>de</strong> <strong>Hibernate</strong>, que utiliza <strong>la</strong>s<br />

anotaciones <strong>de</strong>finidas en <strong>la</strong>s c<strong>la</strong>ses persistentes para obtener los<br />

mappings objeto/re<strong>la</strong>cional<br />

• Cuando se invoca el método <strong>con</strong>figure sin argumentos <strong>de</strong><br />

AnnotationConfiguration, <strong>Hibernate</strong> busca un fichero <strong>con</strong><br />

nombre hibernate.cfg.xml en el CLASSPATH <strong>de</strong> <strong>la</strong> aplicación<br />

• Existe otra versión <strong>de</strong> <strong>con</strong>figure que permite especificar otro nombre <strong>de</strong><br />

fichero<br />

• La inicialización <strong>de</strong>l objeto SessionFactory es un proceso costoso,<br />

que suele hacerse sólo una vez al comienzo <strong>de</strong> <strong>la</strong> aplicación<br />

• Normalmente se utiliza una c<strong>la</strong>se, <strong>Hibernate</strong>Util, para implementar<br />

<strong>la</strong> instancia única <strong>de</strong> este objeto<br />

• En un bloque estático se inicializa <strong>la</strong> instancia <strong>de</strong> SessionFactory que<br />

utilizará <strong>la</strong> aplicación<br />

• El método getSessionFactory <strong>de</strong>vuelve siempre <strong>la</strong> misma instancia <strong>de</strong><br />

<strong>la</strong> c<strong>la</strong>se SessionFactory<br />

• El método shutdown cierra <strong>la</strong> instancia única <strong>de</strong> SessionFactory y<br />

libera todos sus recursos (información <strong>de</strong> mapping, pool <strong>de</strong> <strong>con</strong>exiones,<br />

etc.)


Fichero <strong>de</strong> Configuración (1)<br />

<br />

<br />

<br />

com.mysql.jdbc.Driver<br />

jdbc:mysql://localhost/pojotest<br />

pojo<br />

pojo<br />

<br />

1<br />

<br />

org.hibernate.dialect.MySQLDialect<br />

<br />

true<br />

true<br />

true<br />

<br />

<br />

<br />

<br />


Fichero <strong>de</strong> Configuración (y 2)<br />

• Fichero <strong>de</strong> <strong>con</strong>figuración <strong>de</strong> <strong>Hibernate</strong> – hibernate.cfg.xml<br />

• Configuración <strong>de</strong> Conexión a BD (data source)<br />

• hibernate.<strong>con</strong>nection.driver_c<strong>la</strong>ss, hibernate.<strong>con</strong>nection.url,<br />

hibernate.<strong>con</strong>nection.username, hibernate.<strong>con</strong>nection.password<br />

• Dialecto hibernate<br />

• hibernate.dialect<br />

• Especifica qué variante <strong>de</strong> SQL tiene que generar para comunicarse <strong>con</strong> <strong>la</strong> BD<br />

• Incluye dialectos para multitud <strong>de</strong> BBDD<br />

• Configuración pool <strong>de</strong> <strong>con</strong>exiones (opcional)<br />

• hibernate.<strong>con</strong>nection.pool_size<br />

• Soporte para integrar cualquier pool <strong>de</strong> <strong>con</strong>exiones; incluye soporte nativo para C3P0<br />

• Propieda<strong>de</strong>s para habilitar logging (<strong>de</strong>shabilitado en un entorno en<br />

producción)<br />

• hibernate.show_sql<br />

• Habilita mostrar todas <strong>la</strong>s sentencias SQL ejecutadas por <strong>Hibernate</strong> por <strong>con</strong>so<strong>la</strong><br />

• hibernate.format_sql<br />

• Hace que <strong>la</strong> salida sea más legible, pero ocupa más espacio en pantal<strong>la</strong><br />

• hibernate.use_sql_comments<br />

• <strong>Hibernate</strong> aña<strong>de</strong> comentarios a todas <strong>la</strong>s sentencias SQL generadas para explicar su<br />

origen<br />

• Mapping <strong>de</strong> entida<strong>de</strong>s<br />

• Lista <strong>de</strong> c<strong>la</strong>ses anotadas o <strong>de</strong> ficheros XML <strong>de</strong>finiendo mappings


Ciclo <strong>de</strong> Vida <strong>de</strong> una Entidad (1)<br />

new<br />

<strong>de</strong>lete()<br />

get()<br />

saveOrUpdate()<br />

Transient Persistent Detached<br />

saveOrUpdate()<br />

close()<br />

gc<br />

gc


Ciclo <strong>de</strong> Vida <strong>de</strong> una Entidad (y 2)<br />

• Un objeto es persistent si está asociado a una<br />

Session y tiene correspon<strong>de</strong>ncia en BD<br />

• Un objeto en este estado pue<strong>de</strong> formar parte en<br />

transacciones<br />

• Si durante <strong>la</strong> transacción se realiza algún cambio sobre él<br />

(invocación <strong>de</strong> un método set), <strong>Hibernate</strong> actualiza<br />

automáticamente su estado en BD antes <strong>de</strong> terminar <strong>la</strong><br />

transacción<br />

• Un objeto es <strong>de</strong>tached si no está asociada a una<br />

Session pero tiene correspon<strong>de</strong>ncia en BD<br />

• Un objeto es transient si no está asociado a una<br />

Session y no tiene correspon<strong>de</strong>ncia en BD


Lenguaje <strong>de</strong> Consultas (1)<br />

• La Session también proporciona acceso a <strong>la</strong> API <strong>de</strong><br />

<strong>con</strong>sultas <strong>de</strong> <strong>Hibernate</strong><br />

• session.createQuery(hqlQuery)<br />

• HQL (<strong>Hibernate</strong> Query Language)<br />

• Permite realizar <strong>con</strong>sultas simi<strong>la</strong>res a SQL, en términos <strong>de</strong><br />

c<strong>la</strong>ses persistentes y sus propieda<strong>de</strong>s<br />

• session.createCriteria(entityC<strong>la</strong>ss)<br />

• API <strong>de</strong> Programación para QBC (Query By Criteria)<br />

• QBC es una alternativa a HQL, que al igual que ésta permite<br />

<strong>la</strong>nzar <strong>con</strong>sultas, pero a diferencia <strong>de</strong> el<strong>la</strong>, no es un lenguaje<br />

<strong>de</strong> <strong>con</strong>sultas, sino una API que permite expresar <strong>con</strong>sultas en<br />

términos <strong>de</strong> objetos (existen objetos para hacer JOINs,<br />

restricciones, proyecciones, etc.)<br />

• session.createSQLQuery(sqlQuery)<br />

• SQL (Structured Query Language)<br />

• Sólo <strong>de</strong>be <strong>de</strong> utilizarse cuando <strong>la</strong>s otras opciones no son<br />

válidas (e.g., cuando se necesite utilizar una característica<br />

propia <strong>de</strong>l SQL nativo <strong>de</strong> <strong>la</strong> BD)


Lenguaje <strong>de</strong> Consultas – HQL (2)<br />

• <strong>Hibernate</strong> Query Language (HQL)<br />

• Sintaxis parecida a SQL (para que sea fácil <strong>de</strong> apren<strong>de</strong>r)<br />

• No usa nombres <strong>de</strong> tab<strong>la</strong>s ni columnas, sino referencias a<br />

objetos y propieda<strong>de</strong>s<br />

• E.g.: En<strong>con</strong>trar <strong>la</strong>s cuentas <strong>de</strong>l usuario <strong>con</strong> i<strong>de</strong>ntificador 1<br />

SELECT a FROM Account a WHERE a.userId = 1<br />

• Account es el nombre <strong>de</strong> <strong>la</strong> entidad<br />

• userId es el nombre <strong>de</strong> una propiedad <strong>de</strong> <strong>la</strong> entidad<br />

• La implementación <strong>de</strong> <strong>Hibernate</strong><br />

• Averigua el nombre <strong>de</strong> <strong>la</strong> tab<strong>la</strong> y <strong>la</strong>s columnas correspondientes a<br />

<strong>la</strong>s propieda<strong>de</strong>s haciendo uso <strong>de</strong> <strong>la</strong>s anotaciones insertadas en <strong>la</strong><br />

c<strong>la</strong>se Account<br />

• Traduce <strong>la</strong> <strong>con</strong>sulta HQL a una <strong>con</strong>sulta SQL<br />

• La ejecuta vía JDBC<br />

• Recupera <strong>la</strong>s fi<strong>la</strong>s y <strong>de</strong>vuelve una lista <strong>de</strong> objetos Account


Lenguaje <strong>de</strong> Consultas – HQL (3)<br />

• Ejemplos <strong>de</strong> utilización <strong>de</strong> <strong>la</strong> API <strong>de</strong> <strong>con</strong>sultas HQL<br />

• findByUserId: En<strong>con</strong>trar <strong>la</strong>s cuentas <strong>de</strong> un usuario<br />

getSession().createQuery(<br />

"SELECT a FROM Account a WHERE " +<br />

"a.userId = :userId ORDER BY a.accountId").<br />

setParameter("userId", userId).<br />

setFirstResult(startIn<strong>de</strong>x).<br />

setMaxResults(count).list();<br />

• findAccountOperationsByDate: En<strong>con</strong>trar <strong>la</strong>s operaciones<br />

bancarias para una cuenta, entre dos fechas<br />

getSession().createQuery(<br />

"SELECT o FROM AccountOperation o WHERE " +<br />

"o.account.accountId = :accountId AND " +<br />

"o.date >= :startDate AND o.date


Lenguaje <strong>de</strong> Consultas – HQL (y 4)<br />

• El método createQuery <strong>de</strong>l objeto Session <strong>de</strong>vuelve un<br />

objeto Query<br />

• Las ca<strong>de</strong>nas :XXX son “parámetros nombrados” y se les pue<strong>de</strong> dar<br />

valor <strong>con</strong> los métodos setParameter<br />

• Devuelven el propio objeto Query otra vez, para permitir escribir <strong>de</strong><br />

manera “compacta” <strong>la</strong> <strong>con</strong>strucción <strong>de</strong> <strong>la</strong> <strong>con</strong>sulta y su ejecución<br />

• En el caso <strong>de</strong> fechas, hay que usar <strong>la</strong> variante que recibe un parámetro<br />

que especifica si el valor pasado es un DATE, TIME o TIMESTAMP<br />

(alternativamente es posible usar el método setCalendar)<br />

• Page-by-Page Iterator<br />

• setMaxResults permite especificar el número máximo <strong>de</strong> resultados<br />

• setFirstResult permite especificar el índice <strong>de</strong>l primer resultado<br />

(<strong>de</strong> 0 en a<strong>de</strong><strong>la</strong>nte)<br />

• Ambos métodos <strong>de</strong>vuelven otra vez el objeto Query<br />

• list permite ejecutar una <strong>con</strong>sulta <strong>de</strong> lectura y <strong>de</strong>vuelve una lista<br />

<strong>con</strong> los resultados<br />

• Se pue<strong>de</strong> utilizar uniqueResult cuando se sabe que sólo habrá<br />

un resultado o ninguno (null en este caso).<br />

• Lanza <strong>la</strong> excepción NonUniqueResultException si <strong>la</strong> <strong>con</strong>sulta<br />

<strong>de</strong>vuelve más <strong>de</strong> un resultado


Implementación <strong>de</strong> DAOs <strong>con</strong> <strong>Hibernate</strong> (1)<br />

• Un DAO <strong>de</strong>fine una interfaz para <strong>la</strong>s operaciones <strong>de</strong> persistencia<br />

(métodos CRUD y <strong>de</strong> búsqueda) re<strong>la</strong>cionadas <strong>con</strong> una c<strong>la</strong>se<br />

persistente particu<strong>la</strong>r<br />

• Existen métodos comunes a todas <strong>la</strong>s entida<strong>de</strong>s<br />

• Utilizando Generics (características introducida en Java SE 5.0),<br />

se pue<strong>de</strong> diseñar un DAO genérico <strong>con</strong> <strong>la</strong>s operaciones comunes<br />

para todas <strong>la</strong>s c<strong>la</strong>ses persistentes<br />

• save<br />

• find<br />

• exists<br />

• remove<br />

• Cada entidad persistente tendrá su propio DAO, que exten<strong>de</strong>rá<br />

el genérico para añadir operaciones propias<br />

• Normalmente añadirá operaciones <strong>de</strong> búsqueda utilizando<br />

diferentes criterios


Implementación <strong>de</strong> DAOs <strong>con</strong> <strong>Hibernate</strong> (2)<br />

• No se ha <strong>de</strong>finido el método update en el DAO porque<br />

<strong>Hibernate</strong> actualiza los objetos persistent dirty<br />

automáticamente<br />

• El DAO Genérico asume un ORM <strong>con</strong> estado, es <strong>de</strong>cir, uno que<br />

dispone <strong>de</strong> una sesión en memoria <strong>con</strong> los objetos accedidos<br />

por el caso <strong>de</strong> uso (esos objetos son los objetos en estado<br />

persistent)<br />

• El ORM sabe si los objetos <strong>de</strong> <strong>la</strong> sesión están modificados, si<br />

tienen que ser insertados o eliminados, <strong>de</strong> manera que cuando<br />

se hace el commit (o se fuerza un flush explícitamente), se<br />

<strong>la</strong>nzan <strong>la</strong>s correspondientes sentencias <strong>de</strong><br />

actualización/inserción o eliminación<br />

• No es factible <strong>de</strong>finir un DAO completamente genérico<br />

• Éste es genérico pero asumiendo un ORM <strong>con</strong> estado<br />

• En cualquier caso, una vez <strong>de</strong>finido, facilita el <strong>de</strong>sarrollo <strong>de</strong> los<br />

casos <strong>de</strong> uso (y esa es su principal virtud)


DAO Genérico – Interfaz (3)<br />

• El DAO genérico se encuentra en el módulo pojo-mo<strong>de</strong>lutil<br />

• La interfaz parametrizada <strong>de</strong>l DAO genérico recibe 2<br />

argumentos<br />

• E, es <strong>la</strong> c<strong>la</strong>se persistente para <strong>la</strong> que se implementará el DAO<br />

• PK, <strong>de</strong>fine el tipo <strong>de</strong>l i<strong>de</strong>ntificador <strong>de</strong> <strong>la</strong> c<strong>la</strong>se persistente. El<br />

i<strong>de</strong>ntificador <strong>de</strong>be ser Serializable<br />

• Los métodos están <strong>de</strong>finidos en base a esos parámetros y no<br />

están acop<strong>la</strong>dos a ninguna tecnología <strong>de</strong> persistencia<br />

public interface GenericDao {<br />

void save(E entity);<br />

E find(PK id) throws InstanceNotFoundException;<br />

boolean exists(PK id);<br />

void remove(PK id) throws InstanceNotFoundException;<br />

}


GenericDao<strong>Hibernate</strong> (4)<br />

• Implementación <strong>de</strong>l DAO genérico <strong>con</strong> <strong>Hibernate</strong><br />

+ save(entity : E) : void<br />

+ find (id : PK) : E<br />

+ exists(id : PK) : boolean<br />

+ remove(id : PK) : void<br />

<br />

E, PK : Serializable<br />

GenericDao <br />

GenericDao<strong>Hibernate</strong><br />

- entityC<strong>la</strong>ss : C<strong>la</strong>ss<br />

- sessionFactory : SessionFactory<br />

# getSession() : Session<br />

+ setSessionFactory(sessionFactory : SessionFactory) : void<br />

E, PK : Serializable<br />

Otras implementaciones<br />

posibles


GenericDao<strong>Hibernate</strong> (5)<br />

public c<strong>la</strong>ss GenericDao<strong>Hibernate</strong><br />

implements GenericDao {<br />

private SessionFactory sessionFactory;<br />

private C<strong>la</strong>ss entityC<strong>la</strong>ss;<br />

@SuppressWarnings("unchecked")<br />

public GenericDao<strong>Hibernate</strong>() {<br />

this.entityC<strong>la</strong>ss = (C<strong>la</strong>ss) ((ParameterizedType) getC<strong>la</strong>ss().<br />

getGenericSuperc<strong>la</strong>ss()).getActualTypeArguments()[0];<br />

}<br />

public void setSessionFactory(SessionFactory sessionFactory) {<br />

this.sessionFactory = sessionFactory;<br />

}<br />

protected Session getSession() {<br />

return sessionFactory.getCurrentSession();<br />

}<br />

public void save(E entity) {<br />

getSession().saveOrUpdate(entity);<br />

}


GenericDao<strong>Hibernate</strong> (6)<br />

@SuppressWarnings("unchecked")<br />

public E find(PK id) throws InstanceNotFoundException {<br />

E entity = (E) getSession().get(entityC<strong>la</strong>ss, id);<br />

if (entity == null) {<br />

throw new InstanceNotFoundException(id, entityC<strong>la</strong>ss.getName());<br />

}<br />

return entity;<br />

}<br />

public boolean exists(PK id) {<br />

return getSession().createCriteria(entityC<strong>la</strong>ss).<br />

add(Restrictions.idEq(id)).<br />

setProjection(Projections.id()).<br />

uniqueResult() != null;<br />

}<br />

@SuppressWarnings("unchecked")<br />

public void remove(PK id) throws InstanceNotFoundException {<br />

getSession().<strong>de</strong>lete(find(id));<br />

}<br />

}


GenericDao<strong>Hibernate</strong> (7)<br />

• Para implementar <strong>la</strong> persistencia utilizando <strong>Hibernate</strong>, el DAO<br />

necesita<br />

• Un objeto Session. El método setSessionFactory permite<br />

proporcionar <strong>la</strong> factoría a partir <strong>de</strong> <strong>la</strong> cual se obtendrá <strong>la</strong> sesión<br />

actual (a través <strong>de</strong>l método getCurrentSession)<br />

• En el apartado 3.4 se utilizará inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias para<br />

establecer el valor <strong>de</strong> <strong>la</strong> propiedad sessionFactory<br />

• Conocer <strong>la</strong> c<strong>la</strong>se persistente que es gestionada por el DAO. En el<br />

<strong>con</strong>structor <strong>de</strong>l DAO se utiliza Java reflection para en<strong>con</strong>trar <strong>la</strong><br />

c<strong>la</strong>se <strong>de</strong>l argumento genérico E y almacenar<strong>la</strong> en <strong>la</strong> propiedad<br />

entityC<strong>la</strong>ss<br />

• Una c<strong>la</strong>se java parametrizada (ParameterizedType) dispone<br />

<strong>de</strong> métodos para obtener un array <strong>con</strong> los tipos <strong>de</strong> sus argumentos<br />

(getActualTypeArguments). Aquí interesa el tipo <strong>de</strong>l<br />

primero <strong>de</strong> los argumentos (0)<br />

• NOTA: Es necesario suprimir algunas advertencias <strong>de</strong>l<br />

compi<strong>la</strong>dor respecto a <strong>con</strong>versiones “unchecked”, <strong>de</strong>bido a que<br />

<strong>la</strong>s interfaces <strong>de</strong> <strong>Hibernate</strong> son compatibles <strong>con</strong> Java SE 1.4


GenericDao<strong>Hibernate</strong> (8)<br />

• La implementación <strong>de</strong> los métodos save, find y<br />

remove <strong>de</strong>lega directamente en los métodos <strong>de</strong> <strong>la</strong><br />

API <strong>de</strong> hibernate saveOrUpdate, get y <strong>de</strong>lete<br />

• La implementación <strong>de</strong>l método exists podría<br />

<strong>de</strong>legar en el método get <strong>de</strong> <strong>Hibernate</strong>, pero como<br />

se comentó en el apartado 3.2, por eficiencia se<br />

utiliza una Query especial que sólo recupera <strong>la</strong> c<strong>la</strong>ve<br />

primaria <strong>de</strong>l objeto persistente<br />

• HQL no es suficiente<br />

• Necesita <strong>con</strong>ocer el nombre <strong>de</strong> <strong>la</strong> c<strong>la</strong>ve primaria<br />

• Criteria permite crear <strong>con</strong>sultas <strong>de</strong> forma programática. En<br />

este caso se utiliza para:<br />

• Añadir una <strong>con</strong>dición por c<strong>la</strong>ve primaria<br />

(Restrictions.idEq(id))<br />

• Proyectar el campo c<strong>la</strong>ve primaria (Projections.id())


{Account, AccountOperation}Dao (9)<br />

• Los DAOs específicos para una c<strong>la</strong>se persistente<br />

• Extien<strong>de</strong>n el DAO genérico proporcionando como<br />

argumentos el tipo <strong>de</strong> c<strong>la</strong>se persistente (Account o<br />

AccountOperation) y <strong>de</strong> su c<strong>la</strong>ve primaria (Long)<br />

• Proporcionan métodos adicionales<br />

• AccountDao<br />

• findByUserId<br />

• AccountOperationDao<br />

• getNumberOfOperations<br />

• findByDate<br />

• removeByAccountId


AccountDao (10)<br />

E, PK : Serializable<br />

<br />

GenericDao <br />

+ save(entity : E) : void<br />

+ find (id : PK) : E<br />

+ exists(id : PK) : boolean<br />

+ remove(id : PK) : void<br />

GenericDao<strong>Hibernate</strong><br />

- entityC<strong>la</strong>ss : C<strong>la</strong>ss<br />

- sessionFactory : SessionFactory<br />

<br />

# getSession() : Session<br />

+ setSessionFactory(sessionFactory : SessionFactory) : void<br />

<br />

AccountDao<br />

+ findByUserId(userId : Long, startIn<strong>de</strong>x : int, count : int) : List<br />

<br />

E, PK : Serializable<br />

AccountDao<strong>Hibernate</strong>


AccountDao (11)<br />

public interface AccountDao extends GenericDao {<br />

}<br />

public List findByUserId(Long userId, int startIn<strong>de</strong>x,<br />

int count);<br />

public c<strong>la</strong>ss AccountDao<strong>Hibernate</strong><br />

extends GenericDao<strong>Hibernate</strong><br />

implements AccountDao {<br />

}<br />

@SuppressWarnings("unchecked")<br />

public List findByUserId(Long userId, int startIn<strong>de</strong>x,<br />

int count) {<br />

}<br />

return getSession().createQuery(<br />

"SELECT a FROM Account a WHERE a.userId =:userId " +<br />

"ORDER BY a.accountId").<br />

setParameter("userId", userId).<br />

setFirstResult(startIn<strong>de</strong>x).<br />

setMaxResults(count).list();


AccountOperationDao (12)<br />

<br />

GenericDao <br />

<br />

<br />

AccountOperationDao<br />

+ getNumberOfOperations(accountId : Long, startDate : Calendar,<br />

endDate : Calendar) : int<br />

+ findByDate(accountId : Long, startDate : Calendar, endDate : Calendar,<br />

startIn<strong>de</strong>x : int, count : int) : List<br />

+ removeByAccountId(accountId : Long) : void<br />

GenericDao<strong>Hibernate</strong><br />

AccountOperationDao<strong>Hibernate</strong>


AccountOperationDao (13)<br />

public interface AccountOperationDao<br />

extends GenericDao {<br />

}<br />

public int getNumberOfOperations(Long accountId,<br />

Calendar startDate, Calendar endDate);<br />

public List findByDate(Long accountId,<br />

Calendar startDate, Calendar endDate, int startIn<strong>de</strong>x,<br />

int count);<br />

public void removeByAccountId(Long accountId);


AccountOperationDao (14)<br />

public c<strong>la</strong>ss AccountOperationDao<strong>Hibernate</strong> extends<br />

GenericDao<strong>Hibernate</strong><br />

implements AccountOperationDao {<br />

public int getNumberOfOperations(Long accountId,<br />

Calendar startDate, Calendar endDate) {<br />

long numberOfOperations = (Long) getSession().createQuery(<br />

"SELECT COUNT(o) FROM AccountOperation o WHERE "<br />

+ "o.account.accountId = :accountId AND "<br />

+ "o.date >= :startDate AND o.date


AccountOperationDao (y 15)<br />

@SuppressWarnings("unchecked")<br />

public List findByDate(Long accountId,<br />

Calendar startDate, Calendar endDate, int startIn<strong>de</strong>x,<br />

int count) {<br />

}<br />

}<br />

...<br />

return getSession()<br />

.createQuery(<br />

"SELECT o FROM AccountOperation o WHERE "<br />

+ "o.account.accountId = :accountId AND "<br />

+ "o.date >= :startDate AND o.date


Alcance <strong>de</strong> <strong>la</strong> Session (1)<br />

• WithdrawFromAccount (en es.udc.pojo.minibank.test.<br />

experiments.hibernate) implementa el caso <strong>de</strong> uso <strong>de</strong> retirar dinero <strong>de</strong> una<br />

cuenta utilizando DAOs<br />

AccountDao<strong>Hibernate</strong> accountDao<strong>Hibernate</strong> = new AccountDao<strong>Hibernate</strong>();<br />

accountDao<strong>Hibernate</strong>.setSessionFactory(<strong>Hibernate</strong>Util.getSessionFactory());<br />

AccountDao accountDao = accountDao<strong>Hibernate</strong>;<br />

AccountOperationDao<strong>Hibernate</strong> accountOperationDao<strong>Hibernate</strong> =<br />

new AccountOperationDao<strong>Hibernate</strong>();<br />

accountOperationDao<strong>Hibernate</strong>.<br />

setSessionFactory(<strong>Hibernate</strong>Util.getSessionFactory());<br />

AccountOperationDao accountOperationDao = accountOperationDao<strong>Hibernate</strong>;<br />

Transaction tx = <strong>Hibernate</strong>Util.getSessionFactory().<br />

getCurrentSession().beginTransaction();<br />

try {<br />

/* Find account. */<br />

Account account = accountDao.find(accountId);<br />

/* Modify ba<strong>la</strong>nce. */<br />

double currentBa<strong>la</strong>nce = account.getBa<strong>la</strong>nce();


Alcance <strong>de</strong> <strong>la</strong> Session (2)<br />

if (currentBa<strong>la</strong>nce < amount) {<br />

throw new InsufficientBa<strong>la</strong>nceException(<br />

accountId, currentBa<strong>la</strong>nce, amount);<br />

}<br />

account.setBa<strong>la</strong>nce(currentBa<strong>la</strong>nce - amount);<br />

/* Register account operation. */<br />

accountOperationDao.create(new AccountOperation(account,<br />

Calendar.getInstance(), AccountOperation.Type.WITHDRAW,<br />

amount));<br />

tx.commit();<br />

} catch (RuntimeException e) {<br />

e.printStackTrace();<br />

tx.rollback();<br />

throw e;<br />

} catch (InstanceNotFoundException e) {<br />

e.printStackTrace();<br />

tx.commit();<br />

} catch (InsufficientBa<strong>la</strong>nceException e) {<br />

e.printStackTrace();<br />

tx.commit();<br />

} finally {<br />

<strong>Hibernate</strong>Util.getSessionFactory().getCurrentSession().close();<br />

}<br />

<strong>Hibernate</strong>Util.shutdown();


Alcance <strong>de</strong> <strong>la</strong> Session (y 3)<br />

• WithdrawFromAccount (<strong>con</strong>t)<br />

• Utiliza dos DAOs, accountDao y accountOperationDao<br />

• Antes <strong>de</strong> utilizar los DAOs, es necesario <strong>con</strong>figurarlos<br />

estableciéndoles el objeto SessionFactory, a través <strong>de</strong>l método<br />

setSessionFactory<br />

• Para que el caso <strong>de</strong> uso sea transaccional, <strong>la</strong>s invocaciones a los<br />

métodos <strong>de</strong>l DAO tienen que realizarse <strong>de</strong>ntro <strong>de</strong> <strong>la</strong> misma unidad<br />

<strong>de</strong> trabajo: misma instancia <strong>de</strong> Transaction en <strong>la</strong> misma<br />

instancia <strong>de</strong> <strong>la</strong> Session<br />

• El método getSession <strong>de</strong>l DAO genérico invoca el método<br />

getCurrentSession <strong>de</strong> SessionFactory que permite obtener<br />

<strong>la</strong> Session <strong>de</strong>l <strong>con</strong>texto <strong>de</strong> ejecución actual (Contextual Session)<br />

• La Session se crea <strong>la</strong> primera vez que se invoca este método <strong>de</strong>ntro<br />

<strong>de</strong> un ámbito <strong>de</strong>finido; el resto <strong>de</strong> veces <strong>de</strong>vuelve el objeto<br />

previamente creado<br />

• ¿Cuál es el ámbito <strong>de</strong> <strong>la</strong> sesión actual? Configurable<br />

• En nuestro ejemplo se está <strong>de</strong>volviendo el mismo objeto Session a<br />

todas <strong>la</strong>s invocaciones a getCurrentSession realizadas sobre el<br />

mismo thread<br />

• Hemos añadido <strong>la</strong> siguiente propiedad al fichero <strong>de</strong> <strong>con</strong>figuración <strong>de</strong><br />

hibernate<br />

<br />

thread


Conclusiones<br />

• La API <strong>Hibernate</strong> simplifica <strong>la</strong> gestión <strong>de</strong> <strong>la</strong> persistencia<br />

• Hemos encapsu<strong>la</strong>do <strong>la</strong> lógica <strong>de</strong> persistencia utilizando el patrón<br />

DAO<br />

• Hemos <strong>de</strong>sarrol<strong>la</strong>do un DAO genérico que reutilizaremos para<br />

implementar todos los DAOs<br />

• En el apartado 3.4<br />

• Se utilizará inyección <strong>de</strong> <strong>de</strong>pen<strong>de</strong>ncias (Spring) para proporcionar<br />

un objeto SessionFactory a los DAOs e inyectar los propios<br />

DAOs en los servicios<br />

• La gestión <strong>de</strong> transacciones y sesiones será transparente a los<br />

DAOs, por eso no se ha <strong>con</strong>si<strong>de</strong>rado en su implementación<br />

• El DAO genérico y <strong>la</strong>s diferentes implementaciones utilizan una<br />

c<strong>la</strong>se utilidad para traducir <strong>la</strong>s excepciones que <strong>de</strong>vuelve <strong>Hibernate</strong><br />

en excepciones propias <strong>de</strong> Spring (in<strong>de</strong>pendientes <strong>de</strong>l framework<br />

<strong>de</strong> persistencia utilizado)

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!