Este documento describe diferentes técnicas de mapeo en Hibernate/JPA como @Embedded, @Embeddable, modos de identidad como @Id, @GeneratedValue, @SequenceGenerator, modos de locking como @Version, herencia mediante @Inheritance, relaciones polimórficas, batch processing, servicios, transitividad (cascades), caching, validaciones con Hibernate Validator y Envers para auditoría. Explica recomendaciones sobre performance y también introduce conceptos como multi-tenancy.
3. Embedded
•
•
•
Clases que no tienen relación directa con una tabla (están contenidas dentro
de otra tabla)
o Ejemplo clásico: Usuario y dirección.
Para mapear, @Embeddable para indicar que una clase se puede incluir
dentro de otra y @Embedded para incluir dentro de una clase.
Añadid dirección a las solicitudes
o @Embeddable a la clase dirección (en vez de @Entity)
o y @Embedded en el atributo dirección dentro de solicitud.
5. Modos de identidad
•
•
Identidad de Tabla:
o @Id
o Compuesto:
Varios @Id
@EmbeddedId (clase con @Embeddable y referencia con
@EmbeddedId)
Id en otra clase y @IdClass en la tabla
Probad cualquiera de estas referencias.
6. Modos de identidad
•
•
Por favor, usad una clave subrogada.
Las claves compuestas sólo dan problemas
o Pueden no ser inmutables
o Los requisitos cambian y afectan a las claves
o Son menos eficientes (ocupan más)
o Son menos uniformes
o Son incómodas en Java (varios setters)
o Son peores en Hibernate
7. Modos de identidad
•
•
•
Generación:
o Identidad (DB2, MySQL, SQL Server...)
o Secuencia
o Tabla (hi/lo a una tabla)
o Auto
o 14 algoritmos más
En base a @GeneratedValue y seleccionando la estrategia y el generador
(@SequenceGenerator, entre otros).
Probad @SequenceGenerator para ver las opciones de configuración.
8. Modos de identidad
•
Además de un @Id puedes definir que una columna es un Id Natural:
o … @NaturalId
o buscar por natural id bySimpleNaturalId
9. Modos de identidad
•
•
•
¿Qué es la identidad?
o Identidad de la BD
o Identidad de Java
Para una MISMA sesión, las dos cosas son lo mismo.
Y fuera? Hibernate no garantiza igualdad.
10. Modos de identidad
•
•
Esto es importante en un par de casos sólo:
o Composite Primary Key Class
o 2 instancias de la misma clase en un Set en sesiones diferentes
Implementa equals y hashCode, teniendo en cuenta que dos objetos
diferentes pueden representar el mismo objeto en la base de datos.
11. Modos de identidad
•
•
•
Repetimos...
o Si usas primary keys compuestas
o O si quieres unir entidades detached (luego veremos que es...) que reúsas
en un Set
Deberías implementar equals y hashCode:
o Comparando por igualdad de negocio (columnas y valores que identifican
univocamente a una entidad)
https://community.jboss.org/wiki/EqualsAndHashCode
13. Modos de locking
•
•
•
•
Hasta ahora, sin saberlo, estamos trabajando con un modo last commit wins.
Cuando vamos a trabajar con algo, Hibernate recupera la entidad de la base
de datos. Si cuando guardamos los datos han cambiado, ese usuario pierde
esa información.
Podemos trabajar de forma optimista, al guardar Hibernate comprueba que
no se han tocado las cosas.
Si se han tocado da un error, si no, sigue con su trabajo.
14. Modos de locking
•
Para activarlo, sólo tenemos que definir un atributo de tipo Integer anotado
con @Version.
o Podemos usar un Date también
•
Probadlo!
•
•
En cualquier momento podemos llamar a un lock pesimista con session.lock()
[Avanzado] Si tenéis tiempo... bloquead la inserción de una tabla.
16. Herencia
•
•
•
Estrategias
o Tabla por clase concreta
Con polimorfismo implícito
Con uniones
o Tabla por jerarquía de clases
o Tabla por subclase
o Relaciones polimórficas
+ combinaciones de las anteriores
JPA no soporta todo lo que soporta Hibernate
18. Tabla por clase concreta con
polimorfismo implícito
•
•
Es simple, consultar contra clases concretas
Contrapartidas…
o Polimorfismo
Requiere una consulta por cada subclase concreta
Las asociaciones no se representan bien (no aceptan una FK simple)
o Semánticas compartidas entre tablas
o El esquema relacional no “conoce” la relación
o No se puede hacer una asociación hacia la abstracta (ya que no es una
entidad)
19. Tabla por clase concreta con
polimorfismo implícito
@MappedSuperclass
public abstract class AbstractPago
@Entity
public class PagoPAC extends AbstractPago
@Entity
public class PagoBeca extends AbstractPago
21. Tabla por clase concreta con uniones
•
Permite relaciones hacia la clase abstracta
•
Consultas optimizables por el SGBD
•
Contrapartidas…
o El esquema relacional no “conoce” la relación
22. Tabla por clase concreta con uniones
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractPago
@Entity
public class PagoPAC extends AbstractPago
@Entity
public class PagoBeca extends AbstractPago
24. Tabla por jerarquía
•
La mejor para el polimorfismo y para no polimorfismo
•
Contrapartidas…
o Requiere columnas nullables en las subclases
o Posible desperdicio de espacio
o Requiere discriminador
25. Tabla por jerarquía
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="tipo",
discriminatorType=DiscriminatorType.STRING)
public abstract class AbstractPago
@Entity
@DiscriminatorValue("PAC")
public class PagoPAC extends AbstractPago
@Entity
@DiscriminatorValue("beca")
public class PagoBeca extends AbstractPago
27. Tabla por subclase
•
Basada en FKs
•
Esquema normalizado
•
Contrapartidas…
o Complica el esquema
o Ineficiente en jerarquías complejas*
o Clase padre concreta
28. Tabla por subclase
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class AbstractPago
@Entity
public class PagoPAC extends AbstractPago
@Entity
public class PagoBeca extends AbstractPago
30. Relaciones polimórficas
•
•
•
Permite mapear relaciones que no se podrían de otra forma: asociaciones
polimórficas a clases desde múltiples tablas
Sólo para situaciones muy especiales
Contrapartidas…
o No puede establecer FKs
o Envers no lo soporta ☹
31. Relaciones polimórficas
public interface Cosa
@Entity
public class Beca implements Cosa
@Entity
public class Pago implements Cosa
@Any(metaColumn = @Column(name = "tipo_de_cosa"))
@AnyMetaDef(idType = "long", metaType = "string", metaValues = {
@MetaValue(value = "Beca", targetEntity = Beca.class),
@MetaValue(value = "Pago", targetEntity = Pago.class) })
@JoinColumn(name = "tipo_de_cosa_id")
public Cosa getCosa() {
35. Batch Processing
•
¿Qué pasa si lanzamos esto?
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Petition petition = new Petition(.....);
session.save(petition);
}
tx.commit();
session.close();
36. Batch Processing
•
•
Hibernate se queda sin memoria.
Podemos arreglarlo, haciendo operaciones en batch:
o Activando hibernate.jdbc.batch_size (en persistence/hibernate.cfg.xml) y
poniendolo a un valor entre 10-50
37. Batch Processing
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Petition petition = new Petition(.....);
session.save(petition);
if ( i % 20 == 0 ) { //el valor de la propiedad
session.flush();
session.clear();
}
}
tx.commit();
session.close();
38. Batch Processing
•
•
•
Stateless Session
o Sin caché de primer nivel
o Sin caché de segundo nivel
o Sin dirty checking/write behind
o Sin cascades
o Sin interceptores
o Similar a JDBC
Scrollable
HQL
39. Batch Processing
•
•
En estrategias de Fetch hay una adicional (cuarta)
o @BatchSize, recupera sentencias en bloque.
MUY interesante...
o soluciona el problema de los N+1 fetches.
41. Servicios
•
Un sistema de extensión (similar a plugins) de Hibernate.
o Desde Hibernate 4.0
o Permite el registro de servicios/implementaciones alternativas para, por
ejemplo:
TransactionFactory (transacciones)
JtaPlatform (gestión de JTA)
JndiService
JdbcServices
ConfigurationService
...
43. Transitividad
•
Cascades de JPA:
o CascadeType.PERSIST: cascada de persists
o CascadeType.MERGE: cascadas de merges
o CascadeType.REMOVE: cascade de eliminaciones
o CascadeType.REFRESH: cascade de refresh (volver a leer una entidad de
BD)
o CascadeType.DETACH: cascade de detach (desunir de sesión)
o CascadeType.ALL: todos los de arriba
44. Transitividad
•
Cascades de Hibernate (@Cascade):
o save-update...
o delete
o lock
o replicate (copiar un objeto, muy muy raro)
o evict
o delete-orphan
45. Transitividad
•
•
Delete orphan es interesante:
o Sólo aplica a oneToMany.
o Cuando elimino un elemento hijo de la colección indico que quiero
eliminarlo de la BD porque se ha quedado huérfano.
o orphanRemoval en @OneToMany.
No estoy haciendo esto: session.remove()
padre.geHijos().remove(0)
o No cuenta para Cascade.ALL.
o @OneToMany(orphanRemoval = true)
Probadlo!
46. Transitividad
•
Recomendaciones de cascade:
o Es más lógico en relaciones @OneToOne y @OneToMany (padre con
hijos)
o Si el objeto hijo en un @OneToMany no tiene sentido sin el padre ->
Cascade.ALL y orphanRemoval
o Típicos: persist, save-update...
48. Caching
•
•
•
Caché de primer nivel
o get() y load() me recuperan objetos que tienen en la sesión...
Caché de segundo nivel
Caché de queries
o Hibernate tiene en cuenta cambios en los objetos que se muestran en las
consultas.
o Depende del patrón de uso de la aplicación.
49. Caching
•
Caché de segundo nivel:
o Muy común, Hibernate se integra muy bien con varios proveedores de
caché.
o Típico para tablas de referencia (provincia, localidad, datos maestros...)
o Marcar entidades interesadas (o colecciones): @Cacheable
o @Cache(usage = CacheConcurrencyStrategy.TIPO_DE_CACHING)
50. Caching
•
Tipos de Caching:
o NONE
o READ_ONLY: no se modifican a menudo...
o NONSTRICT_READ_WRITE: pocas transacciones apuntando al mismo
item...
o READ_WRITE: actualizaciones frecuentes.
o TRANSACTIONAL
54. Performance
•
•
Hibernate no es lento.
Recomendaciones de rendimiento:
o Tener en cuenta las 4 estrategias de fetching (Select, Subselect, Join,
BatchSize)
o Cuidado con N+1
o Fetch-profiles (para personalizar escenarios de búsqueda)
o Personalizar Fetch en Criterias
(criteria.setFetchMode("propiedad",MODO)) EAGER=JOIN, LAZY=SELECT
Probadlo!"
55. Performance
•
•
•
Tres grandes tipos de colecciones:
o indexed collections (mapas, listas, arrays)
o sets
o bags
Las dos primeras son las más eficientes en save/remove/update de
elementos.
Bags y lists son más eficientes en @OneToMany con mappedBy
56. Performance
•
•
One-shot-delete
o Imaginemos que tenemos 20 elementos, eliminamos 18 y añadimos 3
o Podemos borrar 18 elementos (1 a 1) e insertar 3
o Eliminar todo e insertar 5
En este caso conviene hacer clear();
Probadlo!
58. Performance
•
Si realiza muchas consultas:
o plantear estrategias estilo JOIN/SUBSELECT
o Activar batch o utilizar @BatchSize
o Activar caché de segundo nivel
o HQL
o SQL
o Query cache
59. Performance
•
Si realiza una consulta muy pesada:
o Eliminar cruces JOIN (empezando por colecciones)
o Poner asociaciones *ToOne a Lazy (con Select o Subselect)
o Índices
o HQL
o SQL
o Query cache
61. Validaciones
•
•
Hibernate se complementa con Hibernate Validator
Es la base de Bean Validation, un estándar Java EE 6
o @NotNull
o @Past
o @Size
o @Pattern
o @Max
o @Email
o @CreditCardNumber
62. Validaciones
•
•
Incluid las librerías de validation (es más sencillo en Java EE)
o Añadid una anotación y probad a guardar.
Podemos llamar a los validadores con:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> constraintViolations =
validator.validate(user);
•
Probadlo!
64. Envers
•
•
•
Librería de auditoría.
Para activar la versión "básica" basta con:
o Copiar la librería
o @Audited
o Cuidado con relaciones: @NotAudited (de momento)
Probadlo!
66. Envers
•
Dos estrategias, start y start-end
•
Puedo guardar el usuario o información adicional
•
Las querys pueden llegar a ser complejas:
List personsAtAddress = getAuditReader().createQuery()
.forEntitiesAtRevision(Person.class, 12)
.addOrder(AuditEntity.property("surname").desc())
.add(AuditEntity.relatedId("address").eq(addressId))
.setFirstResult(4)
.setMaxResults(2)
.getResultList();
67. Envers
•
Puedo preguntar a una entidad por las revisiones que ha tenido
Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity(MyEntity.class, false, true)
.setProjection(AuditEntity.revisionNumber().min())
.add(AuditEntity.id().eq(entityId))
.add(AuditEntity.revisionNumber().gt(42))
.getSingleResult();
•
Probadlo!
68. Envers
•
•
Envers es más quisquilloso en algún tipo concreto de mapeos:
o Bags
o @OneToMany+@JoinColumn, mappedBy (necesita @AuditJoinTable)
o @OneToMany+@JoinColumn, insertable=false, updatable = false
(necesita @AuditMappedBy)
Siempre se puede decidir no auditar una relación con @NotAudited.
70. Multi-tenancy
•
Una aplicación que sirve múltiples clientes.
•
Típico de SaaS (Software as Service: google docs)
•
Cada cliente sólo debe poder ver sus datos
o Base de datos diferente, JDBC que cambia
o Esquema separado, schema que cambia al conectarse
o Única base de datos, discriminador
71. Multi-tenancy
•
•
En Hibernate:
Session session = sessionFactory.withOptions()
.tenantIdentifier( yourTenantIdentifier )
.openSession();
Opciones de identificador:
o DATABASE
o SCHEMA
o DISCRIMINATOR (No soportada aún, Hibernate 5)
76. Recomendaciones
•
•
No utilices claves compuestas
o Claves surrogadas siempre, ni naturales ni compuestas
En el import.sql (ficheros sql que carga Hibernate directamente), los ids,
negativos (para no hacer conflicto con ids generados)
•
No uses herencia, usa agregación (GoF)
•
No uses mapas, usa entidades y relaciones
77. Recomendaciones
•
•
•
•
Cuando estés mapeando por primera vez, hacer tests (incluso vacíos) está
bien:
o No arrancas el servidor de aplicaciones!
Ojo con los "lazos" en los modelos y las relaciones bidireccionales...
Se puede implementar SoftDelete (@SQLDelete) o restricciones globales de
búsqueda (@Where)
Puedo ver los valores de las consultas (?) -> en log4j, org.hibernate.type a
TRACE.
78. Errores típicos
•
Lazy Initialization Exception, causado por:
o Intentar cargar desde la vista/lógica con la sesión cerrada una lista de
objetos/objeto con fetch a LAZY.
o -> Hibernate.initialize()
o -> Activar lazy load sin transacciones:
hibernate.enable_lazy_load_no_trans
o -> OpenSessionInView
o -> Conversaciones
79. Errores típicos
•
•
Detached entity passed to persist, causado por:
o Fijar un id manualmente a una entidad que utiliza un auto-generado
o Sin cascadas, intentar guardar una relación de un objeto que no está en la
base de datos.
o Llamada persist() o save() de un objeto que ya está en la BD
Cannot Open Connection, causado por:
o Malos valores de conexión a la BD.
80. Errores típicos
•
•
•
NPE en IntegerType.next(IntegerType.java:82), causado por:
o Número de versión (@version) nulo
Collection was not processed by flush, causado por (varios motivos,
peliagudo):
o Envers, bags y malas anotaciones de envers.
o Jugar con las listas (utilizar add() a veces y sustituir las listas enteras al
mismo tiempo).
Llamo a persist() y no tengo id, causado por:
o persist() no asigna el id directamente
o -> usa save()
81. Errores típicos
•
•
No row with the given identifier exists (uff), causado por:
o Hay una entidad que apunta a una fila de la BD que ya no existe
o Modificaciones en paralelo de la colección
o Cascades delete/eliminaciones de elementos de la colección sin
actualizar el otro lado de la relación.
Hibernate me devuelve datos duplicados
Relaciones bidireccionales mal mapeadas
Cruces cartesianos sin Distinct.ROOT_ENTITY
•
•
82. Errores típicos
•
A collection with cascade delete-orphan was no longer referenced...
o Mala gestión de las colecciones:
Coleccion coleccion = miEntidad.getColeccion();
Coleccion coleccionNueva = new ColeccionImpl(coleccion);
miEntidad.setColeccion(coleccionNueva);
•
o -> add()
De principiante (y no tanto): constructor, @Entity, @Id...
84. Recapitulando...
•
•
•
•
•
¿Cómo es una clase persistente, una entidad?
o @Entity y @Id (Constructor [Serializable])
¿Cómo se especifica los metadatos de mapeos?
o Anotaciones | XML @OneToMany...
¿Qué relación hay entre identidad de objetos y de base de datos?
o Dentro de una sesión, son lo mismo. Si hay objetos detached -> no.
¿Qué tipos de herencia hay?
o 4 -> tablas única, 2 tablas (2), 3 tablas
¿Cuál es el ciclo de vida de un objeto persistente?
o Persistent -> Detached. Transient nuevo, Delete a eliminar
85. Recapitulando...
•
•
¿Cómo se busca y ordena?
o Criteria, HQL, SQL, anotaciones...
¿Cómo se recuperan objetos con asociaciones?
o @OneToMany... fetches
86. Recapitulando...
•
Preguntas de examen
o get() y load() -> Hibernate | JPA (lazy)
o save() / persist() / saveOrUpdate() -> = que antes, diferencias en
tratamiento en id.
o named SQL Query? -> query definida para reusar.
o SessionFactory? -> objeto único.. thread safe me da sesiones
o Session? Threadsafe? -> no...
o sorted/ordered collections? -> ordenada en memoria o no
87. Recapitulando...
•
Preguntas de examen
o transient/persistent/detached? -> estados de la entidad
o session.lock()? -> marcar entidad buena, bloquear BD
o second level cache? ->
o Constructor sin argumentos? -> no sabe qué crear...
o Hibernate Entity Class Final? -> es malo por rendimiento
o lazy initialization exception? ->
o N+1 selects? ->
89. Ventajas e inconvenientes de los ORMs
•
Ideas?
o Te olvidas de la base de datos (buscar inserts o updates modificados)
o Y del SGBD (usando estándar SQL)
o Simplifica la gestión de inserciones/actualizaciones
o Otra herramienta/capa más
o 'Mapeos complejos'
o Conflictiva si no la usas bien
o ...
90. Ventajas de los ORMs
•
•
•
•
•
Diseño Orientado al Objeto puro (con pegas)
Separación de lógica y datos → Java EE, EJBs...
Productividad
o Operaciones en cascada
o Navegación
o Mapea una vez → CRUD completo
Separación de responsabilidades
Valor añadido (validación, envers...)
91. Inconvenientes de los ORMs
•
•
•
•
•
Necesitas conocimientos de SQL
Conviene “olvidarse del modelo” → modelo relacional “incorrecto”.
Se delega el control de las consultas
Modelo de dominio anémico
o Exposición de la implementación
o Separación de lógica y datos
o …
… “(…) the main thing is to be practical and stop paying too much attention
to lame patterns talk.”