Salta el contingut

2. Introduccio a Hibernate

1. Hibernate

Hibernate

Hibernate és un framework ORM per a Java, que facilita la correspondència d'atributs entre una base de dades relacional i el model d'objectes de la nostra aplicació mitjançant fitxers XML o anotacions en els beans d'entitat. És un programari lliure distribuït sota la llicència GPL 2.0, per la qual cosa es pot utilitzar en aplicacions comercials.

La funció principal de Hibernate serà oferir a l'programador les eines per detallar el seu model de dades i les relacions entre ells, de manera que sigui l'ORM mateix el que interactuï amb la base de dades, mentre que el desenvolupador es dedica a manipular objectes.

A més, ofereix un llenguatge de consulta, anomenat HQL (Hibernate Query Language), de manera que sigui l'ORM mateix el que tradueixi aquest llenguatge al de cada motor de base de dades, mantenint així la portabilitat a costa d'un lleuger augment en el temps d'execució.

Hibernate

Quan es creen objectes, aquests són volàtils o temporals (es destruiran quan l'aplicació finalitzi). Quan volem emmagatzemar-los amb Hibernate, són rastrejats amb un mecanisme anomenat Session. També es rastrejen els objectes carregats des de les bases de dades. Si ho desitgem, podem finalitzar el rastreig, eliminant l'objecte quan ja no el necessitem.

A més, es proporciona un llenguatge de consulta, HQL o Hibernate Query Language, basat en OQL. La part subjacent de la sessió, com es pot veure, permet l'ús de diverses tecnologies, incloent JDBC per connectar-se al SGBD necessari.

2. Configuració

Hibernate, com un bon framework, no necessita una instal·lació, ja que està integrat en el nostre projecte com a llibreries. Podríem optar per instal·lar moltes llibreries jar, però és molt millor utilitzar un gestor de paquets per automatitzar aquesta tasca. El procés de construcció del projecte serà més fàcil al final.

Utilitzarem Maven com a gestor de paquets.

3. Dependencies.

En els nostres projectes s'utilitzaran dues eines bàsiques: Hibernate i un controlador per connectar-se a la base de dades seleccionada. Òbviament, cal afegir les dependències al gestor de paquets. En Maven, les dependències s'inclouen al fitxer Pom.xml, a la carpeta arrel del nostre projecte. Dins de l'etiqueta <dependencies> cal afegir:

XML
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.6.3.Final</version>
</dependency>

Si has triat Gradle com a gestor de paquets, el teu build.gradle hauria de ser el següent:

Bash
1
2
3
4
5
6
7
dependencies {
    // https://mvnrepository.com/artifact/mysql/mysql-connector-java
    implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.27'

    // https://mvnrepository.com/artifact/org.hibernate/hibernate-core
    implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.6.3.Final'
}

Recorda...

Recorda que pots trobar els paquets a https://mvnrepository.com/repos/central

4. Estructura del projecte

Un cop hem afegit les dependències, hem de crear una estructura de projecte per organitzar les nostres classes, amb l'objectiu de separar la lògica del programa. A continuació, mostrarem una breu descripció, aprofundint en cada punt més endavant.

4.1. Beans

Els Beans són l'evolució dels POJOs que hem estudiat en unitats anteriors. Recorda que aquestes classes són objectes comuns, sense pertànyer a cap arbre d'herència i sense implementar cap interfície. S'utilitzen per emmagatzemar informació sobre un concepte concret (si necessito un cotxe...crea un cotxe).

Com a extensió dels POJOs, apareixen els Beans, sense restriccions en els atributs, constructors i herència. Tenen algunes restriccions:

  • Els seus atributs han de ser private (encapsulació).
  • Han de implementar la interfície Serializable.
  • Han de tenir getters i setters públics.
  • Han de tenir un constructor genèric (sense arguments).

Recorda

La serialització en temps d'execució associa a cada classe serialitzable un número de versió, anomenat serialVersionUID, que s'utilitza durant la deserialització per verificar que l'emissor i el receptor d'un objecte serialitzat han carregat classes per a aquest objecte que són compatibles en relació a la serialització.

Si el receptor ha carregat una classe per a l'objecte que té un serialVersionUID diferent del de la classe de l'emissor corresponent, llavors la deserialització donarà lloc a una InvalidClassException. Una classe serialitzable pot declarar el seu propi serialVersionUID explícitament mitjançant la declaració d'un camp anomenat serialVersionUID que ha de ser estàtic, final i de tipus long:

Java
static final long serialVersionUID = 137L;

Així, els Beans són components d'accés a dades i representen entitats a la nostra aplicació. És una bona idea crear els nostres Beans a la mateixa carpeta, normalment anomenada Model.

Recorda

Recordes la llibreria Lombok? És molt útil per crear els nostres beans amb només unes poques línies de codi.

4.2. Fitxers de mapeig

Un cop s'hagin creat les entitats, cal mapejar cada entitat. Cada mapeig estableix les referències entre els beans i les taules, els atributs i les columnes, per establir una coincidència perfecta entre ells.

La primera opció serà crear fitxers de mapeig, amb la sintaxi (en XML) entre les classes i les taules. Si el bean es diu Car, el fitxer de mapeig hauria de ser Car.hbm.xml:

  • hbm és per a mapeig d'hibernate
  • xml perquè la sintaxi està en l'especificació XML.

Estudiarem els fitxers de mapeig en les següents seccions.

4.3. Altres fitxers

Finalment, tindrem la resta de classes del programa, així com l'aplicació o classe principal. Si esteu dissenyant una aplicació amb un entorn gràfic, hi hauria classes de representació de dades o vistes. De manera similar, en cas d'una aplicació web, faltarien els controladors i els serveis.

5. Configuració del projecte

Anem a examinar més detingudament el fitxer de configuració d'Hibernate. En el fitxer de configuració d'Hibernate podem establir les opcions de manera desordenada, però es recomana agrupar els blocs d'opcions per aclarir i mantenir el codi, així com indicar mitjançant comentaris què estem fent en cada moment. Ho veurem amb un exemple complet, que detallarem pas a pas en les seccions següents.

5.1. Hibernate.cfg.xml

XML
<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE hibernate-configuration PUBLIC 
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

 <hibernate-configuration>
    <session-factory>
      <!-- Connection properties -->
      <!-- Driver JDBC -->
        <property name="connection.driver_class">
          com.mysql.cj.jdbc.Driver
        </property>

      <!-- Add ?createDatabaseIfNotExist=true to create database  -->
      <property name="connection.url">
        jdbc:mysql://localhost:3308/DBName
      </property>

      <!--user and pass -->
      <property name="connection.username">root</property>
      <property name="connection.password">root</property>

      <!-- extra conf -->

      <!-- JDBC connection pool for concurrent connections -->
      <property name="connection.pool_size">5</property>

      <!--  dialect connector. Useful for Foreing Keys-->
      <property name="dialect">
        org.hibernate.dialect.MySQL5InnoDBDialect
      </property>

      <!-- one thread one session -->
      <property name="current_session_context_class">thread</property>

      <!-- show "reals" SQL ops. only for development-->
      <property name="show_sql">true</property>

      <!-- DB maintenance -->
      <property name="hbm2ddl.auto">update</property>

      <!-- options hbm2dll:
        create : 
          create always DB when session factory is loaded. Data will be lost.
        update : 
          Data will be safe, but database structure will be update. 
          Useful in production.
        create-drop : 
          like create and dropping the database.
        validate:   
          check the mapping between database and beans.
      -->

      <!-- Mapping files. Can be combined-->

      <!-- mapping classes -->
      <mapping class="package.class1" />
      <mapping class="package.class2" />

      <!-- Maping files-->
      <mapping resource="class3.hbm.xml" />
      <mapping resource="class4.hbm.xml" />
    </session-factory>
</hibernate-configuration>

Cal tenir en compte que:

  • És molt recomanable tenir l'opció de mostrar les consultes SQL establerta a true, almenys en els primers projectes, per veure com es realitza la correspondència dels objectes amb les consultes SQL.
  • L'opció hbm2ddl és molt potent, ja que si només partim del model orientat a objectes, Hibernate crearà la base de dades per a nosaltres (obviament buida de dades). Més endavant veurem en una pràctica posterior una altra opció molt interessant, hbm2java, que mitjançant l'enginyeria inversa ens permetrà crear els nostres Beans a partir del disseny relacional.
  • Els fitxers de mapeig XML () han d'estar junts amb les classes Java, en el mateix paquet.
  • Els mapejos dins de les classes () fan referència als propis Beans, com veurem a la següent secció.

5.2. Càrrega de scripts

Si voleu inserir dades a la vostra base de dades en aplicacions de prova, és una bona idea tenir un script SQL i carregar-lo automàticament quan es carregui la configuració d'Hibernate. Podeu afegir un fitxer sota src/main/resources anomenat import.sql, i, després de la creació de taules, si existeix, aquest script s'executarà. Aquesta és la manera de començar un projecte amb dades de prova existents a les nostres bases de dades.

A més, si voleu executar més scripts, podeu afegir fitxers específics a hibernate.cfg.xml, de la següent manera:

XML
1
2
3
<property name="hibernate.hbm2ddl.import_files">
  /import1.sql, /import2.sql
</property>

6. Carregant la configuració i les sessions

Per tal de carregar el fitxer de configuració anterior, hem de crear un objecte SessionFactory a través del qual podem crear instàncies de Session per connectar-nos a la nostra base de dades. L'estructura d'aquesta classe serà sempre la mateixa: carregar el fitxer de configuració XML i després crear la SessionFactory.

Java
public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    // Código estático. Sólo se ejecuta una vez, como un Singleton
    static {
        try {
            // Creamos es SessionFactory desde el fichero hibernate.cfg.xml 
            sessionFactory = new Configuration()
                .configure(new File("hibernate.cfg.xml")).buildSessionFactory();    
        } catch (Throwable ex) {
            System.err.println("Error en la inicialización.  " + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

Atenció

Aquesta implementació s'ha realitzat amb el patró de disseny Singleton, per tal de tenir una única instància de SessionFactory.