Hibernate - JPA @luce 4

72
Hibernate / JPA @luce4

description

Fourth day of a course about Hibernate, centered in queries and advanced concepts about the Hibernate Session (states, management...)

Transcript of Hibernate - JPA @luce 4

Page 1: Hibernate - JPA @luce 4

Hibernate / JPA @luce4

Page 2: Hibernate - JPA @luce 4

¿Qué deberíamos saber?

• Relaciones simples @OneToMany y @ManyToOne

• Cascades

• Tipos de fetching

Page 3: Hibernate - JPA @luce 4

¿Qué vamos a ver?

• Dudas?

• El código está en github.

Page 4: Hibernate - JPA @luce 4

¿Qué vamos a ver?

• Consultas

• Entender la sesión...

Page 5: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 6: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 7: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 8: Hibernate - JPA @luce 4

Entidades y estados en detalle

• La sesión (y entityManager) hace varias cosas:

o Dirty Checking -> sólo actualizar las cosas que han cambiado.

o Actúa de caché de primer nivel -> sólo lee si no lo tiene en sesión

o Puede asegurar identidad de objetos

o Puede extender la conversación

Page 9: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 10: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 11: Hibernate - JPA @luce 4

Entendiendo el estado Persistent

Page 12: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Para ver el estado Persistent...o Cread un objeto (Pepe)o Guardad (Pepe)o Cambiad un valor del objeto (Lola) y no llamo a save/persist/update...o Haced un commito En otra sesión o en la BD, leed el valor.

Qué pasa?Se persiste el valor

Page 13: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Yo personalmente habría esperado leer Pepe.• No llamamos a update explícitamente• Hay 1 transacción de base de datos• Cuando hacemos un save() y un rollback() no recupera el estado del objeto

en Java (obviamente)o get() -> recupero Pepeo cambio a Lolao rollback() -> en BD sigue Pepeo mi modelo Java sigue poniendo Lola

Page 14: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Para ver el estado Persistent tras un get/loado Recuperad un objeto de BDo Cambiad un valor del objetoo Haced un commit

Qué pasa?-> Sí ha cambiado el valor.

Page 15: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Esta política se llama write-behind.• Implica que las entidades en estado PERSISTENT porque

o o se ha llamado save/update/persist... dentro de una sesión...o o se han recuperado de base de datos con un get/load...

• guardan los cambios hechos hasta que se hace un commit();

• Problemas?• Dudas?

Page 16: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Haced el mismo ejercicio que antes, pero cuando recuperéis la sesión llamad a :o session.setFlushMode(FlushMode.Manual);

• Lo que inserta NO es la transacción es que POR DEFECTO (FlushMode.AUTO), Hibernate hace un flush cuando haces un commit();

Page 17: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Si una entidad está en estado Persistent y hacemos un flush(), los cambios van a la BD

Page 18: Hibernate - JPA @luce 4

Entendiendo el estado Detached

Page 19: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Probadlo!o Abrid una sesión, buscad un objeto, cerrad la sesióno Modificad el objetoo Abrid la sesión, buscadlo de nuevo...

• Ahora tenemos 2 objetos diferentes (en Java) apuntando a la misma fila de la BD...

Page 20: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Problemas con entidades detached e identidad:o Abrid una sesión, buscad un objeto, cerrad la sesióno Modificad el objetoo Abrid la sesión, buscadlo de nuevo...o Guardad el primer objeto (el que ha sido modificado) con un update

-> ERROR!

• Si hago un update() ANTES de buscar...o Todo funciona correctamente, update sincroniza el estado a persistent en

vez de detached.

Page 21: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Este caso es poco habitual pero no tan raro cómo parece:o Típicamente no harás una consulta del elemento directamente y luego un

update().o Pero sí que es más habitual que ALGO haga una consulta que recupere un

elemento detached (típicamente una asociación con Eager, una precarga...)

Page 22: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Puedo pasar de detached a persistent de cuatro formas:o session.update() / session.saveOrUpdate()o session.merge (me crea un objeto "sincronizado")

Probadlo!• load(1), cerrando sesión• modificando• load(1), merge(), update()/saveOrUpdate()

o session.evict() (sacar de la sesión el objeto que acabo de recuperar)o session.lock() (igual que el merge si sé con seguridad que no he tocado el

objeto)

Page 23: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Usually update() or saveOrUpdate() are used in the following scenario:o the application loads an object in the first sessiono the object is passed up to the UI tiero some modifications are made to the objecto the object is passed back down to the business logic tiero the application persists these modifications by calling update() in a

second session

Page 24: Hibernate - JPA @luce 4

Entidades y estados en detalle

• saveOrUpdate() does the following:o if the object is already persistent in this session, do nothingo if another object associated with the session has the same identifier,

throw an exceptiono if the object has no identifier property, save() ito if the object's identifier has the value assigned to a newly instantiated

object, save() ito if the object is versioned by a <version> or <timestamp>, and the version

property value is the same value assigned to a newly instantiated object, save() it

o otherwise update() the object

Page 25: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 26: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Merge() is very different:o if there is a persistent instance with the same identifier currently

associated with the session, copy the state of the given object onto the persistent instance

o if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance

• the persistent instance is returned• the given instance does not become associated with the session, it remains

detached

Page 27: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Otra forma de evitar estos problemas:o LazyInitializationExceptiono An instance with...

• y que no haya nunca entidades detached...o es hacer conversaciones, la sesión está abierta siempre.

Page 28: Hibernate - JPA @luce 4

Patrones

Page 29: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Patrones de transacciones:o Sesión por operación: ANTI PATRÓN.o Sesión por request: la más habitual.o Conversaciones

Versioning Unión de objetos detached

Page 30: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Típico flujo de Hibernate con un patrón de request

Page 31: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Flujo de Hibernate con un patrón conversación

Page 32: Hibernate - JPA @luce 4

Entidades y estados en detalle

• En conversación tendremos VARIAS transacciones (en cada carga de pantalla, vista...), mientras no guardemos nada no hay problema.

• Para no guardar nada intermedio:o Podemos controlar manualmente cuando se hace un flush del contexto

de la Sesión con session.setFlushModeo Hacemos un flush al final de todo y ya.

Page 33: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Problema:o persist(): it does not guarantee that the identifier value will be assigned

to the persistent instance immediately, the assignment might happen at flush time. persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries.

o save() does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not "sequence"), this INSERT happens immediately, no matter if you are inside or outside of a transaction.

Page 34: Hibernate - JPA @luce 4

Entidades y estados en detalle

• El problema sólo ocurre en MySQL/SQL Server si uso save...o es un problema fácilmente mitigable :)

Page 35: Hibernate - JPA @luce 4

Entidades y estados en detalle

Page 36: Hibernate - JPA @luce 4

Entidades y estados en detalle

• ¿Qué pasa con un flush en medio de una transaccion y luego hago un rollback?o setFlushMode(FlushMode.MANUAL)o session.flush()o Probadlo!

• Y si hago: o cambioo flush()o cambioo flush()o rollback()

Page 37: Hibernate - JPA @luce 4

Entidades y estados en detalle

• De las dos formas los cambios no son persistidos.

• Las transacciones deciden si algo se guarda o no.

• Session.flush() decide QUÉ va a la base de datos.

• En el segundo caso (varios flush()) se envían varios updates a la BD (varias sincronizaciones).

Page 38: Hibernate - JPA @luce 4

Entidades y estados en detalle

• Conclusiones:o Con request puro

Tened cuidado con LazyInitiali... (precargas) Cuidado con asociar la misma entidad... (típicamente no hacer

consultas y hacer update al final)o Con OpenSessionInView (sesión abierta en vista)

Cuidado con asociar la misma entidad.o Con Conversaciones

Sin problemas. Hacer un flush al final y ya.

Page 39: Hibernate - JPA @luce 4

Entidades y estados en detalle

• "By default, the persistence context is flushed (synchronized with the database) at the end of each transaction."

• "As the result of a truly stupid and shortsighted decision by certain non-JBoss, non-Sun and non-Sybase members of the EJB 3.0 expert group, there is currently no simple, usable and portable way to implement atomic conversations using EJB 3.0 persistence. However, Hibernate provides this feature as a vendor extension to the FlushModeTypes defined by the specification, and it is our expectation that other vendors will soon provide a similar extension."

• Flush mode manual.

Page 40: Hibernate - JPA @luce 4

Queries

Page 41: Hibernate - JPA @luce 4

Queries

• HQL• Criteria Hibernate• Criteria JPA• SQL

Page 42: Hibernate - JPA @luce 4

Criteria Hibernate

Page 43: Hibernate - JPA @luce 4

Criteria Hibernate

• API clásica de Hibernate (3<)...

• No está deprecated pero sin desarrollo activo.

• No es type safe.

• Trabaja en base a objetos de tipo Criteria.

Page 44: Hibernate - JPA @luce 4

Criteria Hibernate

• La sintaxis es:

o session.createCriteria(___.class).list();o Probad!

Page 45: Hibernate - JPA @luce 4

Criteria Hibernate

• El criterio básico de comparación es un objeto de tipo Restrictions:

o Criteria criteria = session.createCriteria(___.class)

o criteria.add(Restrictions.eq("nombre_de_la_propiedad", valor));

o criteria.list();

• Probadlo!

Page 46: Hibernate - JPA @luce 4

Criteria Hibernate

• Muchos tipos de restrictions:o equalo notEqualo gto geo lto leo betweeno likeo in

Page 47: Hibernate - JPA @luce 4

Criteria Hibernate

• Intentad hacer una comparación que se traslade a algo como:o select * from tabla where upper(nombre) like "%algo%"

ilike (MatchMode.)

• Method Chaining

• if (quieroBuscarPorAlgo) then { criteria.add() }...

Page 48: Hibernate - JPA @luce 4

Criteria Hibernate

• createAlias("solicitudes","solicitud")• Restrictions.eq("solicitud.nombre","LO_QUE_SEA");

o Probadlo! (es el cruce más importante)

• Alias por defecto de la tabla raíz: {alias}

• Disjunction

• addOrder

Page 49: Hibernate - JPA @luce 4

Criteria Hibernate

Disjunction disjunction = Restrictions.disjunction();

Conjunction conjunction = Restrictions.conjunction();

conjunction.add(2 = 1);

conjunction.add(3 = 2);

disjunction.add(conjunction);

disjunction.add(Restrictions.eqOrIsNull(propertyName, value))

criteria.add(disjunction);

// where (2=1 and 3=2) or (eqOrIsNull)

Page 50: Hibernate - JPA @luce 4

Criteria Hibernate

• Comparaciones más complicadas:o sqlRestrictions()

o Subqueries

o Query by example (criteria.add(Example.create(exampleObject)))

Probadlo!

Page 51: Hibernate - JPA @luce 4

Criteria Hibernate

• ¿Qué consultas hacéis?

o Problemas con extensiones del lenguaje (recursividad)o No tenemos insert/update masivo (en HQL sí)

Page 52: Hibernate - JPA @luce 4

Criteria JPA 2

Page 53: Hibernate - JPA @luce 4

Criteria JPA 2

• TypeSafe• JPA (necesitas entityManager)

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Petition> query = builder.createQuery(Petition.class);

Root<Petition> petition = query.from(Petition.class);

query.select(petition);

TypedQuery<Petition> typedQuery = entityManager.createQuery(query);

List<Petition> petitions = typedQuery.getResultList();

• Ejecutadlo!

Page 54: Hibernate - JPA @luce 4

Criteria JPA 2

• Restricciones:

o query.where(builder.equal(petition.get("type"), "elemento"));

Page 55: Hibernate - JPA @luce 4

Criteria JPA 2

• Metamodelo Nos permite hacer restricciones sin cadenas.o Hay que generarlo: 1, 2 y 3

EntityType<Petition> Petition_ = petition.getModel();

query.where(builder.equal(petition.get(Petition_.type), "elemento"));

• Recordad: query.where(builder.equal(petition.get("type"),

"elemento"));

Page 56: Hibernate - JPA @luce 4

Criteria JPA 2

• Joins:o Join<Petition, User> user = petition.join(Petition_.user);

Page 57: Hibernate - JPA @luce 4

HQL

Page 58: Hibernate - JPA @luce 4

HQL

• Hibernate Query Language y Java Persistence Query Language (JPQL)

o Extensiones de SQL para trabajar con objetos, sintaxis abreviada.

o Non-type-safe (basado en cadenas)

o Case Insensitive (da igual las mayúsculas: sELEct)

Page 59: Hibernate - JPA @luce 4

HQL

• El select más sencillo de HQL tiene esta forma:o "from com.nhpatt.user.User" y recupera todos los elementos de la clase

User (todas las filas de la tabla = select * from User).

• Tienen la forma:[select_clause] from_clause [where_clause][groupby_clause][having_clause][orderby_clause]

Page 60: Hibernate - JPA @luce 4

HQL

• Probad un select sin más!o session.createQuery("from User").list();

o session.createCriteria(User.class).list()

• Tienen en cuenta las relaciones de herencia (varias actualizaciones o varias eliminaciones)

• Insert, update y delete

• HQL (y el estándar no) tiene insert

Page 61: Hibernate - JPA @luce 4

HQL

• Ejemplo de Update:

String hqlUpdate = "update Petition p set p.type = :newType where p.type

= :oldType";

int updatedEntities = session.createQuery(hqlUpdate)

.setString( "newType", newType )

.setString( "oldType", oldType )

.executeUpdate();

Page 62: Hibernate - JPA @luce 4

HQL

• Ejemplo de Select con Joins:

select p

from Petition p

join p.user u

where u.name like '25'

Page 63: Hibernate - JPA @luce 4

HQL

• Ignora las estrategias de anotaciones por defecto.

• Left Join FETCHo Estrategia Eager!

Page 64: Hibernate - JPA @luce 4

Native SQL Queries

Page 65: Hibernate - JPA @luce 4

Native SQL Queries

• En cualquier momento podemos lanzar una consulta SQL directamente con:o session.createSQLQuery("_consulta_").list();

• Probadlo!, qué devuelve?o ...

Page 66: Hibernate - JPA @luce 4

Native SQL Queries

• Podemos especificar el tipo de los resultados, para que les convierta explícitamente:o session.createSQLQuery("_consulta_")

.addScalar("ID", Hibernate.LONG)

.addScalar("NAME", Hibernate.STRING)

• Es útil, pero nos gustaría conversiones automáticas a entidades

Page 67: Hibernate - JPA @luce 4

Native SQL Queries

• Hibernate puede convertir el resultado de una query SQL en una entidad (incluso con asociaciones):o session.createSQLQuery("_consulta_").addEntity();

• Asociaciones (eager):

session.createSQLQuery("select * from user u, Petition p WHERE p.userId =

u.id")

.addEntity()

.addJoin();

Page 68: Hibernate - JPA @luce 4

Native SQL Queries

• Asociaciones:select {user.*},{role.*} from Usuario user, Role role

where user.role_id = role.id")

.addEntity("user", User.class)

.addEntity("role", Role.class)

• Es mejor hacer joins.

Page 69: Hibernate - JPA @luce 4

Native SQL Queries

• Hibernate puede transformar resultados de cosas que no son entidades:

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")

.setResultTransformer(Transformers.aliasToBean(CatDTO.class))

Page 70: Hibernate - JPA @luce 4

Native SQL Queries

• Se pueden utilizar parámetros:

query.setString(0, "");

query.setString("name", "");

"select * from tabla where columna = :name"

No se pueden reutilizar nombres, esto no vale:

"select * from tabla where columna = :name and columna2 = :name"

• Named Queries

Page 71: Hibernate - JPA @luce 4

¿Dudas?

Page 72: Hibernate - JPA @luce 4

Hibernate / JPA @luce4