5.1. Configuración#

El primer paso es saber cómo configurar nuestro proyecto para poder usar la especificación JPA. Necesitamos dos cosas:

  1. Importar librerías.

  2. Crear una configuración.

5.1.1. Dependencias#

<properties>
    <maven.compiler.release>21</maven.compiler.release>
    <hibernate.version>7.2.0.Final</hibernate.version>
</properties>

<!-- ... -->

<dependencies>
      <!-- Especificación JPA -->
      <dependency>
          <groupId>jakarta.persistence</groupId>
          <artifactId>jakarta.persistence-api</artifactId>
          <version>3.2.0</version>
      </dependency>

      <!-- Implementación de Hibernate para la especificación anterior -->
      <dependency>
          <groupId>org.hibernate.orm</groupId>
          <artifactId>hibernate-core</artifactId>
          <version>${hibernate.version}</version>
          <scope>runtime</scope>
      </dependency>

      <!-- Driver JDBC para SQLite -->
      <dependency>
          <groupId>org.xerial</groupId>
          <artifactId>sqlite-jdbc</artifactId>
          <version>3.51.1.0</version>
          <scope>runtime</scope>
      </dependency>

      <!-- Soporte en Hibernate para SQLite -->
      <dependency>
          <groupId>org.hibernate.orm</groupId>
          <artifactId>hibernate-community-dialects</artifactId>
          <version>${hibernate.version}</version>
          <scope>runtime</scope>
      </dependency>

      <!-- Opcional para anotaciones de comprobación -->
      <dependency>
          <groupId>jakarta.validation</groupId>
          <artifactId>jakarta.validation-api</artifactId>
          <version>3.1.1</version>
      </dependency>

     <!-- Implementación de Hibernate para la especificación anterior -->
     <dependency>
         <groupId>org.hibernate.validator</groupId>
         <artifactId>hibernate-validator</artifactId>
         <version>9.1.0.Final</version>
          <scope>runtime</scope>
     </dependency>

     <!-- Opcional para evaluación dinámica de cadenas de texto (puede necesitarlo Hibernate Validator) -->
     <dependency>
        <groupId>org.glassfish.expressly</groupId>
        <artifactId>expressly</artifactId>
        <version>6.0.0</version>
        <scope>runtime</scope>
     </dependency>
</dependencies>

En resumidas cuentas es:

  • El soporte para JPA que proporciona Hibernate mediante hibernate-core. Podemos incluir explícitamente , aunque no es necesario, la especificación jakarta.persistence-api (con la prevención que más adelante se refiere).

  • Como en nuestro caso particular utilizaremos SQLite, necesitamos el driver JDBC correspondiente (sqlite-jdbc), y el soporte para Hibernate (hibernate-community-dialects).

  • En principio, basta con lo anterior, pero si se quiere llevar a cabo comprobaciones sobre los valores de los datos (p.ej. las referidas más adelante), será necesaria la implementación para la especificación JPA que proporciona Hibernate, esto es, hibernate-validator. Como anteriormente, podemos incluir explícitamente la implementación (jakarta.validation-api).

  • La última dependencia (expressly) sólo es necesaria si se utilizan anotaciones implementadas por hibernate-validator que aprovechan la evaluación dinámica de cadenas. Por ejemplo:

    @Min(value = 18, message = "La edad debe ser mayor a {value} años")
    private int edad;
    

Prudencia

Obsérvense los scopes de las dependencias. Como nuestra intención es ceñirnos estrictamente a la especificación JPA, las implementaciones de Hibernate están en el scope runtime (véase el comentario al respecto que se hizo sobre sqlite-jdbc). Ahora bien, si se prefiere prescindir de hacer explícita la dependencia a las especificaciones JPA, entonces el scope de las librerías de Hibernate deberá ser compile (o no poner ninguno, que es equivalente), porque, de mantenerlo como runtime, Maven resolverá la dependencia de las especificaciones también con ese scope y no las tendremos disponibles en la compilación de la API.

5.1.2. Configuración de la persistencia#

Debe escribirse en el archivo resources/META-INF/persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0"
    xmlns="https://jakarta.ee/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence 
    https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">

    <!-- Nombre de la unidad de persistencia -->
    <persistence-unit name="MiUnidadP" transaction-type="RESOURCE_LOCAL">

        <!-- Proveedor de JPA (Hibernate) -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!-- Entidades (deberían autodetectarse si están anotadas con @Entity) -->
        <class>edu.acceso.ejemplo.modelo.MiClase</class>
        <!-- Más clases ... -->

        <properties>
            <!-- Propiedades generales (conexión a la base de datos) -->
            <property name="jakarta.persistence.jdbc.driver" value="org.sqlite.JDBC"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:sqlite:file::memory:"/>
            <!-- Innecesarios para SQLite -->
            <property name="jakarta.persistence.jdbc.user" value=""/>
            <property name="jakarta.persistence.jdbc.password" value=""/>

            <!-- Configuración propia de Hibernate -->
            <property name="hibernate.dialect" value="org.hibernate.community.dialect.SQLiteDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="false"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

En este archivo:

  • Debemos recordar el nombre que le damos a la unidad de persistencia, porque la usaremos luego; y hay propiedades cuyos valores dependen del ORM y el SGBD utilizados.

  • El proveedor indica cuál es la implementación JPA que usaremos. Algunos muy usados son:

    Proveedor

    Hibernate

    org.hibernate.jpa.HibernatePersistenceProvider

    EclipseLink

    org.eclipse.persistence.jpa.PersistenceProvider

    OpenJPA

    org.apache.openjpa.persistence.PersistenceProviderImpl

    BatooJPA

    org.batoo.jpa.persistence.PersistenceProviderImpl

  • En caso de que no se detecten, se pueden registrar las clases del modelo cuyos objetos se quieren hacer persistentes.

  • La url, el driver y dialecto específico dependen de cuál sea el SGBD que utilicemos. La enumeración de los más utilizados la tenemos en una tabla de la unidad anterior, pero necesitamos añadir a ella cómo se llaman los dialectos específicos de Hibernate:

    SGBD

    Dialecto

    SQLite

    org.hibernate.community.dialect.SQLiteDialect[1]

    MariaDB

    org.hibernate.dialect.MariaDBDialect

    MySQL

    org.hibernate.dialect.MySQLDialect

    PostgreSQL

    org.hibernate.dialect.PostgreSQLDialect

    Oracle

    org.hibernate.dialect.OracleDialect

    SQL Server

    org.hibernate.dialect.SQLServerDialect

  • La propiedad hibernate.hbm2ddl.auto define cómo actuará la aplicación al comparar el esquema definido en las clases con el existente en la base de datos, y puede tener cuatro valores:

    Valor

    Descripción

    Efecto

    Sin esquema previo

    Con esquema previo

    none

    No hace nada

    -

    -

    validate

    Comprueba el esquema

    Error

    Error si no son iguales

    update

    Actualiza el esquema

    Crea el esquema

    Actualiza y conserva datos

    create

    Crea el esquema

    Crea el esquema

    Lo crea de nuevo (pérdida de datos)

    create-drop

    Crea el esquema y lo desecha al cerrar.

    Como el anterior en ambos casos, pero se pierde todo al cerrar la sesión de Hibernate.

  • Las tres últimas propiedades se refieren al registro y son útiles a efectos de depuración: se registran las sentencias SQL que genera el ORM (en el nivel DEBUG), tales sentencias se formatean para que resulten más legibles y, además, se acompañan de comentarios.

Ahora bien, ¿qué ocurre cuándo los datos de conexión o el tipo de base de datos no están definidos de antemano, sino que se definen en tiempo de ejecución? En ese caso, puede definirse un java.util.HashMap que defina las valores de las propiedades y pasar el mapa cuando se usa la unidad de persistencia para crear el objeto EntityManagerFactory (véase a continuación cómo conectar). En ese caso, las definiciones del mapa sobrescribirán a las que pudieran existir en el archivo. Por ejemplo:

Map<String, String> props = new HashMap<>();
props.put("jakarta.persistence.jdbc.url", "jdbc:sqlite:centro.db");
props.put("hibernate.show_sql", "true");

EntityManagerFactory emf = Persistence.createEntityManagerFactory("MiUnidadP", props);

Advertencia

Es necesario que el archivo META-INF/persistence.xml pase a la compilación, que es lo que debería ocurrir… a menos que haya usted cambiado ese comportamiento al añadir una sección resources al archivo pom.xml.

Notas al pie