m3-uf2 persistencia con hibernate
eugeniaperez.es
Unidad 4: Mapeo de clases persistentes
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.1 Asociaciones
En Hibernate permite representar tipos de relaciones:
- Uno a Uno
- Uno a Muchos
- Muchos a Muchos
Cada una de éstas podrá ser unidireccional o bidireccional.
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Una ocurrencia solo puede relacionarse con una entidad
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Mediante archivos de mapeo XML (address.hbm.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.sistema.hibernate.oneToOne">
<class name="Address" table="address">
<id name="id" column="id">
<generator class="identity" />
</id>
<property name="street" column="street" />
<property name="postCode" column="postCode" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
En el mapeo de Persona queremos reciba el mismo identificador que la Direccion, mediante el generador foreign:
<hibernate-mapping package="org.sistema.hibernate.oneToOne.models">
<class name="Person" table="person">
<id name="id" column="id">
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<property name="name" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Ahora agregaremos la siguiente línea para indicar a través del name cómo se llama el atributo en la clase Persona que indica la relación 1:1 con dirección.
Mediante el atributo cascade podremos indicarle distintas operaciones que se realizarán en los hijos a la vez que en los padres:
<one-to-one name="address" cascade="persist, delete" />
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
La mayoría de los valores corresponde con un método con el mismo nombre, del objeto Session, con excepción de "all" y "none".
<hibernate-mapping package="org.sistema.hibernate.oneToOne.models">
<class name="Person" table="person">
<id name="id" column="id">
<generator class="foreign">
<param name="property">address</param>
</generator>
</id>
<property name="name" />
<one-to-one name="address" cascade="persist, delete" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Finalmente se crea un fichero hibernate.cfg.xml donde se incluyan los dos XML anteriormente creados (el de Address y Person).
<!-- Here comes the mapping definition - saved in resources dir with this
hibernate config -->
<mapping resource="org/sistema/hibernate/oneToOne/mappings/Address.hbm.xml" />
<mapping resource="org/sistema/hibernate/oneToOne/mappings/Person.hbm.xml" />
En este fichero se configurará el nombre de la BD que deberás crear, así como el usuario con permisos para trabajar sobre la misma
UNIDAD 4: mapeo de clases persistentes
4.2 Relaciones 1:1
Se lanza el Main...
sesion.persist(address3);
sesion.persist(person1);
sesion.persist(person2);
...
sesion.delete(person1);
Person person1 = new Person();
person1.setName("Persona que sera borrada");
Person person2 = new Person();
person2.setName("Persona que permanecera");
Address address1 = new Address();
address1.setStreet("Calle 1");
Address address2 = new Address();
address2.setStreet("Calle 2");
person1.setAddress(address1);
person2.setAddress(address2);
Address address3 = new Address();
address3.setStreet("Calle de Prueba de identificadores");
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
El resultado es el esperado...
Tabla Address
Tabla Person
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToOneXml del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetoonexml.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
A continuación haremos lo mismo pero con ANOTACIONES. Es decir, prescindiremos de los XML.
Para ello añadiremos la siguiente dependencia el el pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.5.6-Final</version>
</dependency>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Veamos las anotaciones sobre la clase Address:
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String street;
private String postCode;
UNIDAD 4: mapeo de clases persistentes
4.2 Relaciones 1:1
De la misma forma que antes, indicaremos que existe una relación 1:1 entre Persona y su Dirección.
Análogamente es posible indicar las operaciones que se producirán en cascada:
@Entity
public class Person {
@Id
private long id;
private String name;
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
private Address address;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Finalmente hacemos referencia desde el fichero de configuración de Hibernate a las dos clases Java.
El resultado debería ser el mismo que el del ejemplo anterior.
<mapping class="org.sistema.hibernate.oneToOne.models.Address" />
<mapping class="org.sistema.hibernate.oneToOne.models.Person" />
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToOneAnnotations del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetooneannotations.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Vamos a ver ahora el mismo caso, pero las relaciones serán BIDIRECCIONALES y no unidireccionales.
Country
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Especifico el Country.hbm.xml:
<hibernate-mapping package="org.sistema.hibernate.oneToOne.models">
<class name="Country" table="countries">
<id name="id" column="id">
<generator class="identity" />
</id>
<property name="name" column="name" />
<one-to-one name="president" cascade="persist,delete" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Especifico el President.hbm.xml:
<hibernate-mapping package="org.sistema.hibernate.oneToOne.models">
<class name="President" table="presidents">
<id name="id" column="id">
<generator class="foreign">
<param name="property">country</param>
</generator>
</id>
<property name="name" />
<one-to-one name="country" constrained="true" />
</class>
</hibernate-mapping>
No puede existir un Presidente si antes no existe el País que gobernará.
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Lanzando el Main...
Country country1 = new Country();
country1.setName("China");
Country country2 = new Country();
country2.setName("Corea");
President president1 = new President();
president1.setName("Jiang Zemin");
President president2 = new President();
president2.setName("Kim Dae-Jung");
country1.setPresident(president1); -> president1.setCountry(country1);
country2.setPresident(president2); -> president2.setCountry(country2);
Country country3 = new Country();
country3.setName("Chipre");
//En la clase Country
public void setPresident(President president) {
this.president = president;
president.setCountry(this);
}
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Lanzando el Main...
//Abrimos la session
sesion.persist(country3);
sesion.persist(country1);
sesion.persist(country2);
...
sesion.delete(country1);
...
Persistimos la entidad en la que hemos establecido la cascada
Tabla Country Tabla President
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToOneBidirectionalXML del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetoonebidirectionalxml.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Anotaciones: veamos cómo sería el mismo ejemplo.
Para la clase Country:
@Entity
public class Country {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
private President president;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Y para la clase President:
@Entity
public class President {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne
private Country country;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
Recuerda añadir los dos ficheros en el fichero de configuración de Hibernate:
<mapping class="org.sistema.hibernate.oneToOne.models.Country" />
<mapping class="org.sistema.hibernate.oneToOne.models.President" />
¿Qué sucede al ejecutar el proyecto...?
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.2 Relaciones 1:1
@OneToOne(mappedBy = "president")
private Country country;
¿Qué sucede al ejecutar el proyecto...?
Redundancia cíclica de FK
Para evitarlo se utiliza mappedBy, lo colocamos en el lado inverso al propietario (País -> cascada), y modificamos la clase presidente :
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToOneBidirectionalAnnotations del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetoonebidirectionalannotations.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Una entidad A está relacionada con muchos objetos de la entidad B.
Si es unidireccional solo las personas podrán acceder a los objetos Libros (y no al revés).
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Creo un archivo book.hbm.xml:
eugeniaperez.es
<hibernate-mapping package="org.sistema.hibernate.oneToMany.models">
<class name="Book" table="books">
<id name="id">
<generator class="identity" />
</id>
<property name="title" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Creo un archivo person.hbm.xml:
eugeniaperez.es
<hibernate-mapping package="org.sistema.hibernate.oneToMany.models">
<class name="Person" table="persons">
<id name="id" column="idPerson">
<generator class="identity" />
</id>
<property name="name" />
<list name="books" cascade="all">
<key column="idPerson" />
<index column="book_order" />
<one-to-many class="Book" />
</list>
</class>
</hibernate-mapping>
FK
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Creo un archivo person.hbm.xml:
eugeniaperez.es
<list name="books" cascade="all-delete-orphan">
<key column="idPerson" />
<index column="book_order" />
<one-to-many class="Book" />
</list>
En 1:N se añaden 2 tipos:
- delete-orphan: se borra de la BD al haberse quedado huérfana.
- delete: se borran padre e hijos.
- all-delete-orphan: se aplican todas las cascadas existentes (debido al all), y adicionalmente la de delete-orphan.
FK
I
FK -> Clave foránea al ID
I -> índice de ordenación en listas
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Revisamos el Main...
eugeniaperez.es
Persistimos person1 (id1) y en cascada se persisten los dos libros asociados (book1 - id1 y book2 -id2)
Ahora persistimos person2 (id2) y en cascada libros asociados (book3 - id3 y book4 -id4)
Finalmente borramos person1
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto onetomanyxml del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetomanyxml.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Ahora lo pasaremos a clases java con anotaciones.
Clase Book:
eugeniaperez.es
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String title;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Clase Person:
eugeniaperez.es
@Entity
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Book> books = new ArrayList<Book>();
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToManyAnnotations del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetomanyannotations.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Veremos ahora el mismo ejemplo, pero con relaciones bidireccionales:
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
El archivo XML correspondiente a la clase Person queda intacto:
eugeniaperez.es
<hibernate-mapping package="org.sistema.hibernate.oneToMany.models">
<class name="Person" table="persons">
<id name="id" column="idPerson">
<generator class="identity" />
</id>
<property name="name" />
<list name="books" cascade="all-delete-orphan">
<key column="idPerson" />
<index column="book_order" />
<one-to-many class="Book" />
</list>
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Sin embargo el Book.hbm.xml varía de esta forma:
eugeniaperez.es
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.sistema.hibernate.oneToMany.models">
<class name="Book" table="books">
<id name="id">
<generator class="identity" />
</id>
<property name="title" />
<many-to-one name="person" column="idPerson" />
</class>
</hibernate-mapping>
unidad 4: mapeo de clases persistentes
eugeniaperez.es
Descarga el código en Bitbucket
Descarga el proyecto oneToManyBidirXML del repositorio de Bitbucket:
Usuario:
Psswd:
URL: https://eugenia_perez@bitbucket.org/eugenia_perez/onetomanybidirectionalxml.git
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Finalmente, cómo sería el mismo ejemplo con anotaciones
Par a la clase Person:
eugeniaperez.es
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
mappedBy = "person", orphanRemoval = true)
private List<Book> books = new ArrayList<Book>();
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Y para la clase Book:
eugeniaperez.es
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String title;
@ManyToOne
private Person person;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.3 Relaciones 1:N
Al igual que en las relaciones 1:1 bidireccionales necesitamos evitar la redundancia cíclica de FK (doble actualización)
El dueño es SIEMPRE el lado del muchos -> Libro
Por lo que añadimos mappedBy en el lado inverso -> Persona
Así el lado inverso sabrá qué nombre le representa en el lado dueño:
eugeniaperez.es
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "person",
orphanRemoval = true)
private List<Book> books = new ArrayList<Book>();
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
En ocasiones necesitamos relacionar muchas entidades de un tipo con muchas entidades de otro tipo.
Normalmente los registros en base de datos se relacionan usando una clave foránea de una tabla con el identificador de otra tabla:
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
Esto no puede ser logrado utilizando simplemente claves foráneas en las tablas de las entidades A y B. En estos casos se utiliza una tercera tabla conocida como tabla de join, tabla de enlace, o tabla de unión.
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
En nuestro ejemplo relacionaremos Estudiantes con Asignaturas.
Es una N:M unidireccional:
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
Relaciones unidireccionales con archivos de mapeo.
Fichero Subject.hbm.xml:
eugeniaperez.es
<hibernate-mapping package="org.sistema.hibernate.manyToMany.models">
<class name="Subject" table="subjects">
<id name="id" column="subject_id">
<generator class="identity" />
</id>
<property name="name" />
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
Relaciones unidireccionales con archivos de mapeo.
Fichero Student.hbm.xml:
eugeniaperez.es
<hibernate-mapping package="org.sistema.hibernate.manyToMany.models">
<class name="Student" table="students">
<id name="id" column="student_id">
<generator class="identity" />
</id>
<property name="name" />
<list name="subjects" table="students_subjects" cascade="all-delete-orphan">
<key column="student_id" />
<list-index column="list_order" />
<many-to-many class="Subject" column="subject_id" />
</list>
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
Al final se consiguen tres tablas:
Una para estudiantes: Otra para asignaturas:
eugeniaperez.es
...Y una tabla unión:
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Y cómo sería este ejemplo con anotaciones
Esta es la clase Subject
@Entity
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Esta es la clase Student
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Subject> subjects = new ArrayList<Subject>();
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Veamos ahora las relaciones N:M bidireccionales
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Con archivos de mapeo:
Date cuenta de que la clase java Student permanece igual, sin embargo en Subject se ha añadido una lista de Students:
public class Subject {
private long id;
private String name;
private List<Student> students = new ArrayList<Student>();
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Recuerda el fichero Student.hbm.xml que permanece intacto:
<hibernate-mapping package="org.sistema.hibernate.manyToMany.models">
<class name="Student" table="students">
<id name="id" column="student_id">
<generator class="identity" />
</id>
<property name="name" />
<list name="subjects" table="students_subjects" cascade="all-delete-orphan">
<key column="student_id" />
<list-index column="list_order" />
<many-to-many class="Subject" column="subject_id" />
</list>
</class>
</hibernate-mapping>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
El fichero Subject.hbm.xml quedaría así:
<hibernate-mapping package="org.sistema.hibernate.manyToMany.models">
<class name="Subject" table="subjects">
<id name="id" column="subject_id">
<generator class="identity" />
</id>
<property name="name" />
<list name="students" table="students_subjects" inverse="true">
<key column="subject_id" />
<list-index column="list_order" />
<many-to-many class="Student" column="student_id" />
</list>
</class>
</hibernate-mapping>
Student es el dueño de la relación (cascade).
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Finalmente se presenta el mismo ejemplo de N:M bidireccionales con anotaciones:
o Student.hbm.xml que permanece intacto:
@Entity
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@ManyToMany(mappedBy = "subjects")
private List<Student> students = new ArrayList<Student>();
Si Student es el dueño, en el lado inverso colocamos mappedBy para evitar redundancia de FK
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.4 Relaciones N:M
eugeniaperez.es
Con anotaciones la clase Student permanece intacta
Tras la ejecución deberían permanecer en BD los mismos registros que en casos anteriores
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Colecciones
eugeniaperez.es
Listas: estructuras de datos que permiten duplicados pero en las que se establece un orden. Su interfaz es List y la implementación es ArrayList.
<list name="cars" table="CARS_LIST" cascade="all">
<key column="SHOWROOM_ID" /><!-- FK in the table of Cars -->
<index column="CAR_INDEX" />
<one-to-many class="Car" />
</list>
La principal característica, aparte de la key, es que se introduce un index para manejar la ordenación interna de la colección.
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Colecciones
eugeniaperez.es
Conjuntos: no admiten duplicados, pero no tienen ordenación. La interfaz es Set, mientras que la implementación es HashSet.
<set name="cars" table="CARS_LIST" cascade="all">
<key column="SHOWROOM_ID" /><!-- FK in the table of Cars -->
<one-to-many class="Car" />
</set>
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Colecciones
eugeniaperez.es
Mapas: son estructuras asociativas de claves par-valor, su interfaz es Map y la implementación HashMap.
<map name="cars" table="CARS_LIST" cascade="all">
<key column="SHOWROOM_ID" /><!-- FK in the table of Cars -->
<!-- The type must be specified in lowercase letter -->
<map-key column="CUST_NAME" type="string" />
<one-to-many class="Car" />
</map>
La clave de cada elemento de la colección es un String con el nombre del cliente, mientras que el valor es el objeto coche (Car).
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
eugeniaperez.es
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
eugeniaperez.es
En POO la herencia es el mecanismo más utilizado para alcanzar la reutilización y la extensibilidad.
Objetivo: crear nuevas clases partiendo de una clase o de una jerarquía de clases preexistente, evitando el rediseño, la modificación y verificación.
La herencia facilita la creación de objetos a partir de otros ya existentes heredando todo el comportamiento (métodos) y eventualmente los atributos (variables) de su superclase.
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
eugeniaperez.es
Las tres estrategias de herencia que soporta Hibernate son:
- Una tabla por toda la jerarquía de clases
- Una tabla por cada clase (joins)
- Una tabla por cada clase concreta (uniones)
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
UNIDAD 4: mapeo de clases persistentes
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
eugeniaperez.es
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tabla por jerarquía de clases
- Se crea una sola tabla para todas las instancias
- Con una columna por cada propiedad
- Se utiliza una columna discriminadora para indicar a qué clase pertenece
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tabla por jerarquía de clases
Persona.hbm.xml:
<hibernate-mapping>
<class name="org.sistema.hibernate.inheritanceMappings.models.Persona"
table="personas">
<id name="id">
<generator class="identity" />
</id>
<discriminator column="DISC" type="string" />
<property name="nombre" />
<property name="edad" />
</class>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tabla por jerarquía de clases con ficheros de mapeo
Mapear subclases: Normal.hbm.xml
<hibernate-mapping>
<subclass name="org.sistema.hibernate.inheritanceMappings.models.Normal"
discriminator-value="nrm"
extends="org.sistema.hibernate.inheritanceMappings.models.Persona">
<property name="ocupacion" />
</subclass>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
De la misma forma mapearíamos el resto, con distintos discriminadores...
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tras la ejecución se crean 5 objetos, siendo ésta la tabla
resultante:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tabla por jerarquía de clases usando anotaciones
Se utiliza @Inheritance en la raíz de la clase
Con strategy indicamos el tipo de estrategia
Por ejemplo, en la clase Persona:
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DIS", discriminatorType=DiscriminatorType.STRING)
public abstract class Persona implements Serializable
{
}
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
En las otras clases también necesito especificar el discriminador. Por ejemplo, en Tecnólogo:
@Entity
@DiscriminatorValue(value="TC")
public class Tecnologo extends Persona {
private int aniosDeEstudios;
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Como podemos ver, con esta estrategia pasamos de nuestra jerarquía de clases a una sola tabla, como se ilustra en la siguiente imagen:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Se puede apreciar que la tabla resultante es similar, tras lanzar una aplicación Main similar a la del ejemplo anterior:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla para cada clase -> joins
En esta estrategia de herencia se creará una tabla por cada una de las clases.
La representación de la relación de herencia entra estas tablas se hace mediante una clave foránea.
Cada tabla contiene solamente las columnas que representan los atributos declarados en la clase, junto con una columna para la clave primaria que es también una clave foránea de la super clase.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Las tablas padre y las hijas son unidas por sus claves primarias compartidas.
Es posible recuperar de la base de datos las instancias haciendo un "join" entre dichas tablas.
Ventaja: las modificaciones de una clase no afectan a los datos almacenados de las otras clase (normalización)
Desventaja: las búsquedas debe realizarse en cada una de las tablas de las clases que componen la jerarquía, que en este caso serían 5.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Text
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla para cada clase usando ficheros de mapeo
Así queda el fichero Persona.hbm.xml:
<hibernate-mapping>
<class name="org.sistema.hibernate.inheritanceMappings.models.Persona"
table="personas">
<id name="id">
<generator class="identity" />
</id>
<property name="nombre" />
<property name="edad" />
</class>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Haríamos lo mismo con Tecnólogo. Para las clases que hereden de él, por ejemplo Programador.hbm.xml:
<hibernate-mapping>
<joined-subclass
name="org.sistema.hibernate.inheritanceMappings.models.Programador"
table="programadores"
extends="org.sistema.hibernate.inheritanceMappings.models.Tecnologo">
<key column="id" />
<property name="lenguajeFavorito" />
<property name="aniosDeExperiencia" />
</joined-subclass>
</hibernate-mapping>
No lleva Id implícito, solo una FK al padre
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla para cada subclase usando ficheros de mapeo
Así queda el fichero Normal.hbm.xml:
<hibernate-mapping>
<joined-subclass
name="org.sistema.hibernate.inheritanceMappings.models.Normal" table="normales"
extends="org.sistema.hibernate.inheritanceMappings.models.Persona">
<key column="id" />
<property name="ocupacion" />
</joined-subclass>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Probemos que funciona correctamente usando el código que teníamos anteriormente en el método "main".. Comprobemos que efectivamente se haya creada una sola tabla para cada una de nuestras clases:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla para cada clase usando anotaciones
En "Persona" usamos la anotación "@Inheritance".
Para usar esta estrategia, lo indicamos en el atributo "strategy" con el valor "InheritanceType.JOINED".
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Persona implements Serializable
{
}
¡En las clases hijas no hay que indicar nada más!
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Ahora comprobemos los datos que contiene cada una de las tablas:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Modificamos nuestra aplicación para recuperar el primer "Programador" que guardamos, al final del método "main":
programadorDAO.find(programador1.getId(), Programador.class);
Y el SQL generado -> joins...
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
SQL resultante mediante joins:
select programado0_.id as id1_1_0_,
programado0_2_.edad as edad2_1_0_,
programado0_2_.nombre as nombre3_1_0_,
programado0_1_.aniosDeEstudios as aniosDeE1_3_0_,
programado0_.aniosDeExperiencia as aniosDeE1_2_0_,
programado0_.lenguajeFavorito as lenguaje2_2_0_
from Programador programado0_
inner join Tecnologo programado0_1_ on programado0_.id=programado0_1_.id
inner join Persona programado0_2_ on programado0_.id=programado0_2_.id
where programado0_.id=?
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
¿Pero qué ocurriría si no sabemos exactamente a cuál de las subclases de "Persona" pertenece la entidad? En ese caso:
new GenericDAO<Persona>().find(programador1.getId(), Persona.class);
NO estamos seguros de a cuál clase pertenece el objeto que queremos recuperar (bueno, nosotros sabemos que buscamos un "Pogramador", pero Hibernate solo sabe que busca una subclase de "Persona").
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
4.5 Entidades, herencia y asociaciones
Hibernate genera la siguiente consulta:
select persona0_.id as id1_1_0_, persona0_.edad as edad2_1_0_, persona0_.nombre as nombre3_1_0_,
persona0_1_.ocupacion as ocupacio1_0_0_, persona0_2_.aniosDeEstudios as aniosDeE1_3_0_,
persona0_3_.aniosDeExperiencia as aniosDeE1_2_0_, persona0_3_.lenguajeFavorito as lenguaje2_2_0_,
persona0_4_.herramientaDeTesteo as herramie1_4_0_,
case
when persona0_3_.id is not null then 3
when persona0_4_.id is not null then 4
when persona0_1_.id is not null then 1
when persona0_2_.id is not null then 2
when persona0_.id is not null then 0
end as clazz_0_
from Persona persona0_
left outer join Normal persona0_1_ on persona0_.id=persona0_1_.id
left outer join Tecnologo persona0_2_ on persona0_.id=persona0_2_.id
left outer join Programador persona0_3_ on persona0_.id=persona0_3_.id
left outer join Tester persona0_4_ on persona0_.id=persona0_4_.id
where persona0_.id=?
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Esta estrategia es muy lenta si tenemos consulta en los que no sabemos el tipo concreto de la clase que buscamos, solo la clase base. A estas consultas se les conoce como "Polimórficas".
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta -> uniones
Se generará una tabla por cada una de las entidades no-abstractas que tenga nuestra aplicación.
Cada tabla tendrá una columna para cada uno de los atributos de la clase de la entidad que almacena, propios y heredados.
Las tablas no están relacionadas de ninguna forma, por lo que terminaremos con un conjunto de tablas independientes una de otras.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta -> uniones
El esquema de la base de
datos será:
No existe tabla Persona
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta -> uniones
Esta estrategia tiene la ventaja de que no es necesario hacer un grupo de joins para obtener todos los datos de una entidad, lo que nuevamente nos funciona si haremos consultas SQL a mano.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando archivos de mapeo
Indicamos que la clase Persona es abstracta con el atributo abstract de <class>.
<hibernate-mapping>
<class name="org.sistema.hibernate.inheritanceMappings.models.Persona"
abstract="true">
<id name="id">
<generator class="assigned" />
</id>
<property name="nombre" />
<property name="edad" />
</class>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando archivos de mapeo
En las subclases en lugar de <class> usaremos <union-subclass> e indicamos de qué clase extiende usando el atributo "extends"
<hibernate-mapping>
<union-subclass
name="org.sistema.hibernate.inheritanceMappings.models.Normal" table="normales"
extends="hibernate.herencia.modelo.Persona">
<property name="ocupacion" />
</union-subclass>
</hibernate-mapping>
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Tras ejecutar el main solo se crearon tablas para las entidades no-abstractas de nuestra aplicación.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando anotaciones
En esta estrategia hacemos uso de la anotación "@Inheritance" en la clase base ("Persona").
Para indicar el mecanismo de herencia que queremos usar utilizamos el atributo "strategy" con valor: InheritanceType.TABLE_PER_CLASS
En lugar de especificar GenerationType.IDENTITY en el identificador colocaremos GenerationType.TABLE.
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando anotaciones
Resultando así la clase Persona:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Persona implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private String nombre;
private int edad;
Y no hace falta nada más en las clases hijas
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando anotaciones
El mapeo de subclases sería idéntico.
Al ejecutar el mismo código Main que el ejemplo anterior, resulta:
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Una tabla por cada clase concreta usando anotaciones
Se genera, además de las tablas esperadas, una tabla llamada "hibernate_sequences", usada para generara los identificadores de cada una de las subclases de "Persona" almacenados en la BD.
Como en el caso anterior las búsquedas no suponen un problema si sabemos de qué entidad se trata, pero en caso de buscar Persona => ¡consultas muy lentas!
UNIDAD 4: MAPEO DE CLASES PERSISTENTES
eugeniaperez.es
4.5 Entidades, herencia y asociaciones
Cómo elegir una estrategia de herencia:
- Si nunca hacemos consultas polimórficas -> opción 3
- Si sí se hacen consultas polimórficas pero las subclases incluyen pocos atributos -> opción 1
- Si sí se hacen consultas polimórficas y las subclases incluyen muchos atributos distintos -> opción 2
Por defecto, utilizaremos la opción 3 para problemas simples.
m3-uf2 persistencia con hibernate
eugeniaperez.es
FIN DE LA UNIDAD 4: Mapeo de clases persistentes
Unidad 4
By eugenia_perez
Unidad 4
- 3,425