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 ...
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)