Saltar a contenido

UT13. Ejemplo Hibernate

alt text

Introducción

Vamos a ver un ejemplo creando un proyecto en IntelliJ para trabajar con Hibernate en el que persistiremos una clase Coche.

La Aplicación crea la entidad Coche (matrícula, marca, modelo, número de plazas) y la persiste en una BD llamada Taller.

Podéis descargar el ejemplo y probarlo

Código Ejemplo Hibernate

Crear la base de datos

Vamos a trabajar con MySQL o MariaDB, donde es necesario crear la base de datos Taller previamente.

CREATE DATABASE TALLER;

Crear proyecto IntelliJ

Es necesario trabajar con Maven, por lo que creamos el proyecto eligiendo Maven

alt text

Maven

alt text

Maven es una herramienta que ayuda a organizar y gestionar proyectos Java. Si quieres usar Hibernate o cualquier librería tienes que:

Sin Maven

  • buscar el .jar en internet
  • descargarlo
  • añadirlo manualmente al proyecto

Con Maven

  • Solo escribes el nombre de la librería
  • Maven la descarga automáticamente
  • También descarga lo que esa librería necesita

El archivo más importante es pom.xml en el que se define:

  • las librerías
  • la versión de Java
  • el nombre del proyecto

Necesitamos incluir las dependencias para trabajar con Hibernate conectando con MariaDB en el fichero pom.xml

alt text

El fichero quedará como sigue:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Taller</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.4.4.Final</version>
        </dependency>

        <!-- API Jakarta Persistence -->
        <dependency>
            <groupId>jakarta.persistence</groupId>
            <artifactId>jakarta.persistence-api</artifactId>
            <version>3.1.0</version>
        </dependency>

        <!-- Driver MariaDB -->
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>3.3.3</version>
        </dependency>
    </dependencies>

</project>
Si apareciera en rojo, pulsar con el botón dcho sobre el fichero, buscar el menú maven y actualizar.

Estructura del proyecto

Vamos a crear un ejemplo simple, pero la estructura ideal sería utilizar un patrón MVC como hemos visto anteriormente.

La estructura simple será:

Taller/
 ├─ src/
 │   ├─ main/
 │   │   ├─ java/
 │   │   │   └─ taller/
 │   │   │      ├─ Main.java
 │   │   │      └─ Coche.java
 │   │   └─ resources/
 │   │      └─ META-INF/
 │   │         └─ persistence.xml
 └─ pom.xml

Crear persistence.xml

Es necesario crear la carpeta META-INFy el fichero persistence.xml

alt text

El fichero contiene

persistence.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!-- Configuración de JPA (Jakarta Persistence) -->
<!-- Ubicación del esquema XML incluida en xsi:schemaLocation -->
<persistence 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"
             version="3.0">

   <!-- Unidad de persistencia -->
   <persistence-unit name="default">

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

      <!-- Clase entidad que se va a mapear -->
      <class>taller.Coche</class>

      <properties>

         <!-- Driver JDBC de MariaDB -->
         <property name="jakarta.persistence.jdbc.driver"
                   value="org.mariadb.jdbc.Driver"/>

         <!-- URL de conexión a la base de datos -->
         <property name="jakarta.persistence.jdbc.url"
                   value="jdbc:mariadb://localhost:3306/taller"/>

         <!-- Usuario de la base de datos -->
         <property name="jakarta.persistence.jdbc.user" value="root"/>

         <!-- Contraseña -->
         <property name="jakarta.persistence.jdbc.password" value=""/>

         <!-- Acción sobre la base de datos al iniciar -->
         <!-- create: crea la tabla si es necesario -->
         <property name="jakarta.persistence.schema-generation.database.action"
                   value="create"/>

      </properties>
   </persistence-unit>
</persistence>

Crear la Entidad

Creamos al Entity Coche que permitirá persistir los objetos Coche. Vamos a mantener las sentencia SQL necesarias como consultas con nombre. De esta manera las tenemos centralizadas en un único lugar.

La clave primaria será la matrícula.

Coche.java
package taller;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;

import java.io.Serializable;

@Entity
@NamedQueries({
        //permite saber si hay coches para poder insertar unos ejemplos
        @NamedQuery(
                name = "Coche.contarTodos",
                query = "SELECT COUNT(c) FROM Coche c"
        ),
        //permite objener el listado de coches
        @NamedQuery(
                name = "Coche.listarTodos",
                query = "SELECT c FROM Coche c"
        ),
        // permite obtener el listado de coches de una marca
        @NamedQuery(
                name = "Coche.buscarPorMarca",
                query = "SELECT c FROM Coche c WHERE LOWER(c.marca) = LOWER(:marca)"
        )
})
//la clase tiene que ser serializable
public class Coche implements Serializable {
    //Para no equivocarnos al llamar a las consultas por nombre, creamos
    //constantes con los nombres anteriores
    static final String CONTAR_TODOS="Coche.contarTodos";
    static final String LISTAR_TODOS="Coche.listarTodos";
    static final String BUSCAR_POR_MARCA="Coche.buscarPorMarca";
    //EL parámetro de la consulta anterior
    static final String BUSCAR_POR_MARCA_marca="marca";



    //la clave primaria será la matrícula
    @Id
    private String matricula;

    private String marca;
    private String modelo;
    private int num_plazas;
    //constructor necesario
    public Coche() {
    }

    public Coche(String matricula, String marca, String modelo, int num_plazas) {
        this.matricula = matricula;
        this.marca = marca;
        this.modelo = modelo;
        this.num_plazas = num_plazas;
    }

    public void setMatricula(String matricula) {
        this.matricula = matricula;
    }

    public String getMatricula() {
        return matricula;
    }

    public String getMarca() {
        return marca;
    }

    public void setMarca(String marca) {
        this.marca = marca;
    }

    public String getModelo() {
        return modelo;
    }

    public void setModelo(String modelo) {
        this.modelo = modelo;
    }

    public int getNum_plazas() {
        return num_plazas;
    }

    public void setNum_plazas(int num_plazas) {
        this.num_plazas = num_plazas;
    }

    @Override
    public String toString() {
        return String.format("%-12s %-12s %-15s %-10d",
                matricula, marca, modelo, num_plazas);
    }
}

CRUD de Coche

Creamos un programa simple que permite el CRUD de la clase Coche. Mostramos un menú con las diferentes opciones para el CRUD

alt text

Main.java
package taller;

import jakarta.persistence.*;

import java.util.List;
import java.util.Scanner;

/**
 * Aplicación principal para gestionar coches en un taller.
 *
 * Este programa implementa un CRUD completo usando JPA:
 * - Create  → crearCoche()
 * - Read    → leerCoche(), mostrarCoches() y buscarPorMarca()
 * - Update  → modificarCoche()
 * - Delete  → borrarCoche()
 */
public class Main {

    /** Fábrica de EntityManager (crea conexiones). */
    private static EntityManagerFactory emf;

    /** EntityManager: permite interactuar con la base de datos. */
    private static EntityManager em;

    /** Scanner único para toda la aplicación (entrada por teclado). */
    private static final Scanner sc = new Scanner(System.in);

    /**
     * Lee un número entero desde teclado con control de errores.
     * Evita que el programa falle si el usuario introduce texto.
     */
    private static int leerEntero(String mensaje) {
        int numero = 0;
        boolean correcto = false;

        while (!correcto) {
            try {
                System.out.print(mensaje);
                numero = Integer.parseInt(sc.nextLine().trim());
                correcto = true;
            } catch (NumberFormatException e) {
                System.out.println("Error: debes introducir un número entero.");
            }
        }

        return numero;
    }

    /**
     * Lee un texto obligatorio (no puede estar vacío).
     */
    private static String leerTextoObligatorio(String mensaje) {
        String texto;

        do {
            System.out.print(mensaje);
            texto = sc.nextLine().trim();

            if (texto.isEmpty()) {
                System.out.println("Error: este campo no puede estar vacío.");
            }
        } while (texto.isEmpty());

        return texto;
    }

    /**
     * Método auxiliar para hacer rollback si algo falla.
     * Se usa cuando ocurre un error en una transacción.
     */
    private static void rollbackSeguro(EntityTransaction tx) {
        try {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }
        } catch (Exception e) {
            System.out.println("Error al deshacer la transacción.");
        }
    }

    /**
     * Comprueba si hay coches en la base de datos.
     * Si no hay ninguno, inserta 6 coches de ejemplo.
     */
    private static void inicializarCoches() {
        EntityTransaction tx = null;

        try {
            Long total = em.createNamedQuery(Coche.CONTAR_TODOS, Long.class)
                    .getSingleResult();

            if (total == 0) {
                System.out.println("No hay coches en la base de datos.");
                System.out.println("Insertando 6 coches de ejemplo...");

                tx = em.getTransaction();
                tx.begin();

                em.persist(new Coche("1111AAA", "Seat", "Cordoba", 5));
                em.persist(new Coche("2222BBB", "Ford", "Focus", 5));
                em.persist(new Coche("3333CCC", "Seat", "Ibiza", 5));
                em.persist(new Coche("4444DDD", "BMW", "Serie 3", 5));
                em.persist(new Coche("5555EEE", "Audi", "A4", 5));
                em.persist(new Coche("6666FFF", "Audi", "A3", 5));

                tx.commit();

                System.out.println("Coches de ejemplo insertados correctamente.");
            }
        } catch (Exception e) {
            rollbackSeguro(tx);
            System.out.println("Error al inicializar los coches.");
        }
    }

    /**
     * Muestra la cabecera de la tabla para los listados.
     */
    private static void mostrarCabeceraTabla() {
        System.out.printf("%-12s %-12s %-15s %-10s%n",
                "MATRICULA", "MARCA", "MODELO", "PLAZAS");
        System.out.println("-----------------------------------------------------");
    }

    /**
     * CREATE → Inserta un nuevo coche en la base de datos.
     */
    private static void crearCoche() {

        EntityTransaction tx = null;
        boolean valido = true;

        try {
            System.out.println("\nALTA DE NUEVO COCHE");

            String matricula = leerTextoObligatorio("Matrícula: ");
            String marca = leerTextoObligatorio("Marca: ");
            String modelo = leerTextoObligatorio("Modelo: ");
            int numPlazas = leerEntero("Número de plazas: ");

            if (numPlazas <= 0) {
                System.out.println("Error: número de plazas inválido.");
                valido = false;
            }

            if (valido && em.find(Coche.class, matricula) != null) {
                System.out.println("Error: ya existe un coche con esa matrícula.");
                valido = false;
            }

            if (valido) {
                Coche c = new Coche(matricula, marca, modelo, numPlazas);

                tx = em.getTransaction();
                tx.begin();

                em.persist(c);

                tx.commit();

                System.out.println("Coche creado correctamente.");
            }

        } catch (PersistenceException e) {
            rollbackSeguro(tx);
            System.out.println("Error de base de datos.");
        } catch (Exception e) {
            rollbackSeguro(tx);
            System.out.println("Error inesperado.");
        }
    }

    /**
     * READ → Consulta un coche por su matrícula.
     */
    private static void leerCoche() {
        try {
            System.out.println("\nCONSULTA DE COCHE");

            String matricula = leerTextoObligatorio("Matrícula: ");

            Coche c = em.find(Coche.class, matricula);

            if (c != null) {
                mostrarCabeceraTabla();
                System.out.printf("%-12s %-12s %-15s %-10d%n",
                        c.getMatricula(), c.getMarca(), c.getModelo(), c.getNum_plazas());
            } else {
                System.out.println("No existe.");
            }

        } catch (Exception e) {
            System.out.println("Error al consultar.");
        }
    }

    /**
     * UPDATE → Modifica un coche existente.
     */
    private static void modificarCoche() {

        EntityTransaction tx = null;
        boolean valido = true;

        try {
            System.out.println("\nMODIFICACIÓN DE COCHE");

            String matricula = leerTextoObligatorio("Matrícula: ");

            Coche c = em.find(Coche.class, matricula);

            if (c == null) {
                System.out.println("No existe.");
            } else {
                System.out.println("Datos actuales:");
                mostrarCabeceraTabla();
                System.out.printf("%-12s %-12s %-15s %-10d%n",
                        c.getMatricula(), c.getMarca(), c.getModelo(), c.getNum_plazas());

                String marca = leerTextoObligatorio("Nueva marca: ");
                String modelo = leerTextoObligatorio("Nuevo modelo: ");
                int plazas = leerEntero("Plazas: ");

                if (plazas <= 0) {
                    System.out.println("Error: número de plazas inválido.");
                    valido = false;
                }

                if (valido) {
                    tx = em.getTransaction();
                    tx.begin();

                    c.setMarca(marca);
                    c.setModelo(modelo);
                    c.setNum_plazas(plazas);

                    tx.commit();

                    System.out.println("Modificado correctamente.");
                }
            }

        } catch (Exception e) {
            rollbackSeguro(tx);
            System.out.println("Error al modificar.");
        }
    }

    /**
     * DELETE → Elimina un coche.
     */
    private static void borrarCoche() {

        EntityTransaction tx = null;

        try {
            System.out.println("\nBORRAR COCHE");

            String matricula = leerTextoObligatorio("Matrícula: ");

            Coche c = em.find(Coche.class, matricula);

            if (c == null) {
                System.out.println("No existe.");
            } else {
                tx = em.getTransaction();
                tx.begin();

                em.remove(c);

                tx.commit();

                System.out.println("Borrado correctamente.");
            }

        } catch (Exception e) {
            rollbackSeguro(tx);
            System.out.println("Error al borrar.");
        }
    }

    /**
     * READ (LISTADO) → Muestra todos los coches en formato tabulado.
     */
    public static void mostrarCoches() {
        try {
            Query q = em.createNamedQuery(Coche.LISTAR_TODOS, Coche.class);

            List<Coche> lista = q.getResultList();

            if (lista.isEmpty()) {
                System.out.println("No hay coches registrados.");
            } else {
                System.out.println("\nLISTADO DE COCHES");
                mostrarCabeceraTabla();

                for (Coche c : lista) {
                    System.out.printf("%-12s %-12s %-15s %-10d%n",
                            c.getMatricula(), c.getMarca(), c.getModelo(), c.getNum_plazas());
                }
            }

        } catch (Exception e) {
            System.out.println("Error al listar.");
        }
    }

    /**
     * READ → Busca coches de una marca concreta.
     */
    private static void buscarPorMarca() {
        try {
            System.out.println("\nBÚSQUEDA POR MARCA");

            String marcaBuscada = leerTextoObligatorio("Marca: ");
            //Construimos la Select
            Query q = em.createNamedQuery(Coche.BUSCAR_POR_MARCA, Coche.class);
            //incluimos el parámetro
            q.setParameter(Coche.BUSCAR_POR_MARCA_marca, marcaBuscada);

            List<Coche> lista = q.getResultList();

            if (lista.isEmpty()) {
                System.out.println("No hay coches de la marca " + marcaBuscada + ".");
            } else {
                System.out.println("\nCOCHES DE LA MARCA: " + marcaBuscada.toUpperCase());
                mostrarCabeceraTabla();

                for (Coche c : lista) {
                    System.out.printf("%-12s %-12s %-15s %-10d%n",
                            c.getMatricula(), c.getMarca(), c.getModelo(), c.getNum_plazas());
                }
            }

        } catch (Exception e) {
            System.out.println("Error al buscar por marca.");
        }
    }

    /**
     * Muestra el menú por pantalla.
     */
    private static void mostrarMenu() {
        System.out.println("\n****************************");
        System.out.println("  TALLER DE COCHES SEVERO");
        System.out.println("****************************");
        System.out.println("1. Alta");
        System.out.println("2. Consulta");
        System.out.println("3. Modificar");
        System.out.println("4. Borrar");
        System.out.println("5. Listar");
        System.out.println("6. Buscar por marca");
        System.out.println("7. Salir");
    }

    /**
     * Método principal.
     */
    public static void main(String[] args) {

        try {
            emf = Persistence.createEntityManagerFactory("default");
            em = emf.createEntityManager();

            inicializarCoches();

            int opcion;

            do {
                mostrarMenu();
                opcion = leerEntero("Opción: ");

                switch (opcion) {
                    case 1 -> crearCoche();
                    case 2 -> leerCoche();
                    case 3 -> modificarCoche();
                    case 4 -> borrarCoche();
                    case 5 -> mostrarCoches();
                    case 6 -> buscarPorMarca();
                    case 7 -> System.out.println("Saliendo de la aplicación...");
                    default -> System.out.println("Opción no válida.");
                }

            } while (opcion != 7);

        } catch (PersistenceException e) {
            System.out.println("Error al conectar con la BD.");
        } finally {
            if (em != null) em.close();
            if (emf != null) emf.close();
            sc.close();
        }
    }
}