Saltar al contenido principal

TP4.1 - Integración de PostgreSQL con Jakarta Persistence

Migración a una Base de Datos de Producción​

Objetivos​

Este trabajo práctico le permitirá:

  • Integrar PostgreSQL con Spring Boot
  • Utilizar Jakarta Persistence (JPA) para la persistencia de datos
  • Configurar Hibernate como ORM
  • Crear y gestionar una base de datos relacional
  • Comprender la diferencia entre Jakarta y Javax Persistence
  • Dominar la configuración con application.properties y application.yml

Tabla de Contenidos​

  1. Repaso Rápido
  2. Adición de la Base de Datos
  3. Prueba con PostgreSQL
  4. Ejercicio 1: Javax Persistence
  5. Ejercicio 2: Configuración YAML
  6. Ejercicio 3: Lombok
  7. Ejercicio 4: Recreación Automática
  8. Ejercicio 5: Otra Base de Datos

1. Repaso Rápido​

Contexto​

En los trabajos prácticos anteriores, creamos una aplicación CRUD completa con H2. Ahora, vamos a migrar a PostgreSQL, una base de datos relacional profesional adecuada para producción.

Objetivos de este TP​

  1. Migrar de H2 a PostgreSQL - Pasar de una base embebida a una base de producción
  2. Utilizar Jakarta Persistence (JPA) - Mapear objetos Java a tablas SQL
  3. Configurar PostgreSQL - Instalar y configurar una base de datos profesional

Requisitos Previos​

  • TP3 (Arquitectura CRUD) completado
  • PostgreSQL instalado (versión 12 o superior)
  • pgAdmin o DBeaver para gestionar PostgreSQL
  • Visual Studio Code con extensiones Java

2. Adición de la Base de Datos​

2.1 Adición de Dependencias en pom.xml​

Abra su archivo pom.xml y agregue las siguientes dos dependencias en la sección <dependencies>:

<!-- Driver PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<!-- Spring Data JPA (incluye Jakarta Persistence) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Explicación:

DependenciaRol
postgresqlDriver JDBC para conectarse a PostgreSQL
spring-boot-starter-data-jpaIncluye JPA, Hibernate y Spring Data

Nota: Después de agregar, VS Code le sugerirá recargar el proyecto Maven. Acepte o ejecute:

mvn clean install -DskipTests

2.2 Configuración de application.properties​

Cree o modifique el archivo src/main/resources/application.properties:

# URL de conexión a la base de datos
spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db?useSSL=false

# Credenciales de conexión
spring.datasource.username=postgres
spring.datasource.password=postgres

# Configuración de Hibernate
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Estrategia de generación de esquema
spring.jpa.hibernate.ddl-auto=create

# Mostrar las consultas SQL (útil para desarrollo)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Explicación de las propiedades:

PropiedadDescripciónValores posibles
spring.datasource.urlURL de conexión JDBCFormato: jdbc:postgresql://host:port/database
spring.datasource.usernameNombre de usuario de PostgreSQLPor defecto: postgres
spring.datasource.passwordContraseña de PostgreSQLAdaptar según su instalación
spring.jpa.hibernate.ddl-autoEstrategia de creación de esquemanone, validate, update, create, create-drop
spring.jpa.show-sqlMostrar las consultas SQL en la consolatrue o false

Valores de ddl-auto:

Importante: Cambie greeting_db por el nombre de su base de datos si es necesario.

2.3 Anotación de la Entidad Greeting.java​

Modifique la clase Greeting.java para convertirla en una entidad JPA:

package com.example.demo;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "greeting")
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

// Constructor por defecto (requerido por JPA)
public Greeting() {
}

// Constructor con parámetros
public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}

// Getters y Setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Explicación de las anotaciones:

AnotaciónRol
@EntityMarca la clase como entidad JPA (mapeada a una tabla)
@Table(name = "greeting")Especifica el nombre de la tabla SQL
@IdMarca el campo como clave primaria
@GeneratedValue(strategy = GenerationType.IDENTITY)Auto-incremento del ID
@Column(name = "content", nullable = false)Configuración de la columna SQL

Arquitectura JPA:

2.4 Creación del Repository​

Cree una interfaz GreetingRepository.java en el paquete com.example.demo:

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
// Spring Data JPA genera automáticamente la implementación
// Métodos disponibles: save(), findAll(), findById(), delete(), etc.
}

Métodos generados automáticamente:

MétodoDescripción
save(Greeting g)Guardar o actualizar
findAll()Recuperar todas las entradas
findById(Long id)Recuperar por ID
deleteById(Long id)Eliminar por ID
count()Contar las entradas

2.5 Modificación del Controller​

Modifique GreetingController.java para usar el repository:

package com.example.demo;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();

private final GreetingRepository greetingRepository;

public GreetingController(GreetingRepository greetingRepository) {
this.greetingRepository = greetingRepository;
}

@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value="name") String name) {
Greeting greeting = new Greeting(
counter.incrementAndGet(),
String.format(template, name)
);

// Guardar en la base de datos
return greetingRepository.save(greeting);
}
}

Flujo de datos:

2.6 Creación de la Base de Datos PostgreSQL​

Opción A: Con pgAdmin​

  1. Abrir pgAdmin

  2. Crear un usuario (opcional):

    • Clic derecho en "Login/Group Roles"
    • "Create" → "Login/Group Role"
    • Pestaña "General": Name = postgres
    • Pestaña "Definition": Password = postgres
    • Pestaña "Privileges": Marcar "Can login?" y "Create databases?"
    • Guardar
  3. Crear la base de datos:

    • Clic derecho en "Databases"
    • "Create" → "Database"
    • Database name: greeting_db
    • Owner: postgres
    • Guardar

Opción B: Con SQL (línea de comandos)​

-- Crear la base de datos
CREATE DATABASE greeting_db;

-- Crear el usuario (si es necesario)
CREATE USER postgres WITH PASSWORD 'postgres';

-- Otorgar privilegios
GRANT ALL PRIVILEGES ON DATABASE greeting_db TO postgres;

Para ejecutar estos comandos:

# Windows
psql -U postgres

# macOS/Linux
sudo -u postgres psql

3. Prueba con PostgreSQL​

3.1 Verificación de la Configuración​

Antes de iniciar la aplicación, verifique:

Lista de verificación:

  • PostgreSQL está iniciado
  • La base de datos greeting_db existe
  • El usuario postgres tiene los privilegios correctos
  • El archivo application.properties es correcto
  • La clase Greeting tiene la anotación @Entity
  • La interfaz GreetingRepository está creada
  • El GreetingController utiliza el repository

3.2 Inicio de la Aplicación​

En VS Code:

Método 1: Vía Terminal​

# Limpiar y compilar
mvn clean install

# Iniciar la aplicación
mvn spring-boot:run

Método 2: Vía Interfaz​

  1. Abrir GreetingApplication.java
  2. Hacer clic en "Run" sobre el método main
  3. O presionar F5

Logs esperados:

Hibernate: drop table if exists greeting cascade
Hibernate: create table greeting (id bigserial not null, content varchar(255) not null, primary key (id))
Started GreetingApplication in X.XXX seconds

3.3 Prueba de los Endpoints​

Prueba 1: Crear un greeting

http://localhost:8080/greeting?name=John

Respuesta esperada:

{
"id": 1,
"content": "Hello, John!"
}

Prueba 2: Crear varios greetings

http://localhost:8080/greeting?name=Alice
http://localhost:8080/greeting?name=Bob
http://localhost:8080/greeting?name=Charlie

3.4 Verificación en PostgreSQL​

Con pgAdmin:​

  1. Abrir pgAdmin
  2. Navegar: Servers → PostgreSQL → Databases → greeting_db → Schemas → public → Tables
  3. Clic derecho en "greeting" → "View/Edit Data" → "All Rows"

Con SQL:​

-- Conectarse a la base
\c greeting_db

-- Mostrar todas las entradas
SELECT * FROM greeting;

-- Resultado esperado:
-- id | content
-- ---+----------------
-- 1 | Hello, John!
-- 2 | Hello, Alice!
-- 3 | Hello, Bob!
-- 4 | Hello, Charlie!

3.5 Logs SQL en la Consola​

Gracias a spring.jpa.show-sql=true, verá las consultas SQL en la consola:

Hibernate: insert into greeting (content) values (?)

4. Ejercicio 1: Repetir con Javax Persistence​

Enunciado​

Repita el proyecto utilizando Javax Persistence en lugar de Jakarta Persistence (versión antigua de JPA).

Objetivo​

Comprender la diferencia entre Jakarta y Javax Persistence.

Contexto Histórico​

Instrucciones​

Paso 1: Modificar pom.xml

Agregue la dependencia Javax Persistence:

<!-- Adición de Javax Persistence -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>

Paso 2: Modificar los imports en Greeting.java

Reemplace:

import jakarta.persistence.*;

Por:

import javax.persistence.*;

Paso 3: Clase completa con Javax

package com.example.demo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "greeting")
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

public Greeting() {}

public Greeting(Long id, String content) {
this.id = id;
this.content = content;
}

// Getters y Setters
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Paso 4: Probar

  1. Recompile: mvn clean install -DskipTests
  2. Reinicie la aplicación
  3. Verifique que todo funcione

Preguntas de Reflexión​

  1. ¿Cuál es la diferencia principal entre Javax y Jakarta Persistence?
  2. ¿Por qué se recomienda Jakarta para nuevos proyectos?
  3. ¿Se pueden mezclar ambos en el mismo proyecto?

5. Ejercicio 2: Configuración con application.yml​

Enunciado​

Convierta su configuración application.properties al formato YAML.

Objetivo​

Dominar el formato YAML para la configuración de Spring Boot.

Instrucciones​

Paso 1: Eliminar application.properties

Elimine o renombre el archivo application.properties.

Paso 2: Crear application.yml

Cree src/main/resources/application.yml:

spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db?useSSL=false
username: postgres
password: postgres

jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true

Paso 3: Probar

Reinicie la aplicación y verifique que todo funcione.

Comparación​

application.properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/greeting_db
spring.datasource.username=postgres
spring.jpa.hibernate.ddl-auto=create

application.yml:

spring:
datasource:
url: jdbc:postgresql://localhost:5432/greeting_db
username: postgres
jpa:
hibernate:
ddl-auto: create

Ventajas de YAML:

  • Más legible para estructuras jerárquicas
  • Menos repetición
  • Formato moderno

6. Ejercicio 3: Simplificación con Lombok​

Enunciado​

Elimine los getters, setters y constructores manuales utilizando Lombok.

Objetivo​

Reducir el código repetitivo con Lombok.

Requisitos Previos​

Paso 1: Agregar la dependencia Lombok en pom.xml

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

Paso 2: Instalar la extensión Lombok en VS Code

  1. Extensiones → Buscar "Lombok"
  2. Instalar "Lombok Annotations Support for VS Code"

Instrucciones​

Modificar Greeting.java:

package com.example.demo;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "greeting")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "content", nullable = false)
private String content;

// Lombok genera automáticamente:
// - Getters
// - Setters
// - toString()
// - equals() y hashCode()
// - Constructor vacío
// - Constructor con todos los argumentos
}

Anotaciones de Lombok:

AnotaciónGenera
@DataGetters, Setters, toString, equals, hashCode
@NoArgsConstructorConstructor vacío
@AllArgsConstructorConstructor con todos los campos
@GetterSolo los getters
@SetterSolo los setters

Prueba​

  1. Recompile
  2. Verifique que la aplicación funcione
  3. Los métodos se generan en compilación

7. Ejercicio 4: Recreación Automática de la Base​

Enunciado​

Configure la aplicación para que recree automáticamente la base de datos en cada inicio.

Objetivo​

Comprender las diferentes estrategias de gestión de esquema.

Instrucciones​

En application.properties (o application.yml):

spring.jpa.hibernate.ddl-auto=create-drop

o

spring:
jpa:
hibernate:
ddl-auto: create-drop

Comparación de Estrategias​

EstrategiaComportamientoUso
noneNinguna acciónProducción
validateValida el esquema sin modificarProducción
updateActualiza el esquemaDesarrollo
createCrea el esquema al inicioPruebas
create-dropCrea al inicio, elimina al detenerPruebas unitarias

Atención​

create-drop elimina todos los datos al detener la aplicación.

Uso recomendado:

# Desarrollo
spring:
jpa:
hibernate:
ddl-auto: update

# Producción
spring:
jpa:
hibernate:
ddl-auto: validate

8. Ejercicio 5: Migración a MySQL, SQL Server u Oracle​

Enunciado​

Repita la integración completa con otra base de datos.

Elección de Base de Datos​

Opción A: MySQL​

Dependencia en pom.xml:

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

Configuración application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/greeting_db?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update

Creación de la base en MySQL:

CREATE DATABASE greeting_db;
CREATE USER 'root'@'localhost' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON greeting_db.* TO 'root'@'localhost';
FLUSH PRIVILEGES;

Opción B: SQL Server​

Dependencia en pom.xml:

<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>

Configuración application.properties:

spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=greeting_db
spring.datasource.username=sa
spring.datasource.password=YourStrong@Password
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.hibernate.ddl-auto=update

Creación de la base en SQL Server:

CREATE DATABASE greeting_db;
GO

ALTER LOGIN sa WITH PASSWORD = 'YourStrong@Password';
GO

USE greeting_db;
GO

Opción C: Oracle​

Dependencia en pom.xml:

<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>

Configuración application.properties:

spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=system
spring.datasource.password=oracle
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
spring.jpa.hibernate.ddl-auto=update

Creación de la base en Oracle:

CREATE USER greeting_user IDENTIFIED BY oracle;
GRANT ALL PRIVILEGES TO greeting_user;

Tabla Comparativa​

Base de DatosPuerto por DefectoDialecto HibernateDificultad
PostgreSQL5432PostgreSQLDialectFácil
MySQL3306MySQLDialectFácil
SQL Server1433SQLServerDialectMedia
Oracle1521Oracle12cDialectDifícil

Instrucciones Generales​

  1. Instale la base de datos elegida
  2. Modifique pom.xml con el driver adecuado
  3. Modifique application.properties con la configuración correcta
  4. Cree la base de datos y el usuario
  5. Recompile y pruebe

Resolución de Problemas​

Problema 1: Connection Refused​

Síntoma:

org.postgresql.util.PSQLException: Connection to localhost:5432 refused

Soluciones:

  1. Verifique que PostgreSQL esté iniciado:

Windows:

# En Servicios, buscar PostgreSQL
services.msc

Linux:

sudo systemctl status postgresql
sudo systemctl start postgresql
  1. Verifique el puerto en postgresql.conf

Problema 2: Authentication Failed​

Síntoma:

PSQLException: FATAL: password authentication failed for user "postgres"

Soluciones:

  1. Verifique la contraseña en application.properties
  2. Restablezca la contraseña:
ALTER USER postgres WITH PASSWORD 'nuevo_mot_de_passe';

Problema 3: Database Does Not Exist​

Síntoma:

PSQLException: FATAL: database "greeting_db" does not exist

Solución:

Crear la base de datos:

CREATE DATABASE greeting_db;

Problema 4: Table Already Exists​

Síntoma:

Table "greeting" already exists

Solución:

Cambie la estrategia DDL:

spring.jpa.hibernate.ddl-auto=update

Problema 5: Lombok No Funciona​

Síntoma:

error: cannot find symbol
method getId()

Soluciones:

  1. Instale la extensión Lombok en VS Code
  2. Reinicie VS Code
  3. Verifique que la dependencia esté en pom.xml
  4. Recompile: mvn clean install -DskipTests spring-boot:run

Preguntas de Repaso​

  1. ¿Cuál es la diferencia entre Jakarta y Javax Persistence?

  2. ¿Qué hace la anotación @Entity?

  3. ¿Cuál es la diferencia entre create, update y validate para ddl-auto?

  4. ¿Cómo genera Spring Data JPA la implementación del repository?

  5. ¿Qué hace la anotación @GeneratedValue(strategy = GenerationType.IDENTITY)?

  6. ¿Por qué debe haber un constructor vacío en una entidad JPA?

  7. ¿Qué genera la anotación @Data de Lombok?

  8. ¿Qué configuración usar en producción para ddl-auto?


Entregables​

A entregar:

  1. Código fuente completo con PostgreSQL funcional
  2. Capturas de pantalla mostrando:
    • La aplicación en ejecución
    • Los datos en pgAdmin
    • Las consultas SQL en la consola
  3. Al menos 2 ejercicios completados
  4. Documento de respuestas a las preguntas de repaso

Criterios de Evaluación​

CriterioPuntos
Integración PostgreSQL funcional25%
Entidad Greeting correctamente anotada20%
Repository creado y utilizado15%
Datos persistidos en PostgreSQL20%
Ejercicios completados10%
Preguntas de repaso respondidas10%
Total100%

Próximos Pasos​

En el próximo TP, veremos:

  • TP3: Creación de una API CRUD completa
  • Operaciones: Create, Read, Update, Delete
  • Gestión de errores
  • Validación de datos

Recursos Adicionales​

Documentación​

Tutoriales​


Volver al Módulo 2