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
- Repaso Rápido
- Adición de la Base de Datos
- Prueba con PostgreSQL
- Ejercicio 1: Javax Persistence
- Ejercicio 2: Configuración YAML
- Ejercicio 3: Lombok
- Ejercicio 4: Recreación Automática
- 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
- Migrar de H2 a PostgreSQL - Pasar de una base embebida a una base de producción
- Utilizar Jakarta Persistence (JPA) - Mapear objetos Java a tablas SQL
- 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:
| Dependencia | Rol |
|---|---|
postgresql | Driver JDBC para conectarse a PostgreSQL |
spring-boot-starter-data-jpa | Incluye 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:
| Propiedad | Descripción | Valores posibles |
|---|---|---|
spring.datasource.url | URL de conexión JDBC | Formato: jdbc:postgresql://host:port/database |
spring.datasource.username | Nombre de usuario de PostgreSQL | Por defecto: postgres |
spring.datasource.password | Contraseña de PostgreSQL | Adaptar según su instalación |
spring.jpa.hibernate.ddl-auto | Estrategia de creación de esquema | none, validate, update, create, create-drop |
spring.jpa.show-sql | Mostrar las consultas SQL en la consola | true 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ón | Rol |
|---|---|
@Entity | Marca la clase como entidad JPA (mapeada a una tabla) |
@Table(name = "greeting") | Especifica el nombre de la tabla SQL |
@Id | Marca 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étodo | Descripció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
-
Abrir pgAdmin
-
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
-
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_dbexiste - El usuario
postgrestiene los privilegios correctos - El archivo
application.propertieses correcto - La clase
Greetingtiene la anotación@Entity - La interfaz
GreetingRepositoryestá creada - El
GreetingControllerutiliza 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
- Abrir
GreetingApplication.java - Hacer clic en "Run" sobre el método
main - 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:
- Abrir pgAdmin
- Navegar: Servers → PostgreSQL → Databases → greeting_db → Schemas → public → Tables
- 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
- Recompile:
mvn clean install -DskipTests - Reinicie la aplicación
- Verifique que todo funcione
Preguntas de Reflexión
- ¿Cuál es la diferencia principal entre Javax y Jakarta Persistence?
- ¿Por qué se recomienda Jakarta para nuevos proyectos?
- ¿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
- Extensiones → Buscar "Lombok"
- 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ón | Genera |
|---|---|
@Data | Getters, Setters, toString, equals, hashCode |
@NoArgsConstructor | Constructor vacío |
@AllArgsConstructor | Constructor con todos los campos |
@Getter | Solo los getters |
@Setter | Solo los setters |
Prueba
- Recompile
- Verifique que la aplicación funcione
- 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
| Estrategia | Comportamiento | Uso |
|---|---|---|
none | Ninguna acción | Producción |
validate | Valida el esquema sin modificar | Producción |
update | Actualiza el esquema | Desarrollo |
create | Crea el esquema al inicio | Pruebas |
create-drop | Crea al inicio, elimina al detener | Pruebas 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 Datos | Puerto por Defecto | Dialecto Hibernate | Dificultad |
|---|---|---|---|
| PostgreSQL | 5432 | PostgreSQLDialect | Fácil |
| MySQL | 3306 | MySQLDialect | Fácil |
| SQL Server | 1433 | SQLServerDialect | Media |
| Oracle | 1521 | Oracle12cDialect | Difícil |
Instrucciones Generales
- Instale la base de datos elegida
- Modifique pom.xml con el driver adecuado
- Modifique application.properties con la configuración correcta
- Cree la base de datos y el usuario
- Recompile y pruebe
Resolución de Problemas
Problema 1: Connection Refused
Síntoma:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused
Soluciones:
- Verifique que PostgreSQL esté iniciado:
Windows:
# En Servicios, buscar PostgreSQL
services.msc
Linux:
sudo systemctl status postgresql
sudo systemctl start postgresql
- Verifique el puerto en
postgresql.conf
Problema 2: Authentication Failed
Síntoma:
PSQLException: FATAL: password authentication failed for user "postgres"
Soluciones:
- Verifique la contraseña en
application.properties - 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:
- Instale la extensión Lombok en VS Code
- Reinicie VS Code
- Verifique que la dependencia esté en pom.xml
- Recompile:
mvn clean install -DskipTests spring-boot:run
Preguntas de Repaso
-
¿Cuál es la diferencia entre Jakarta y Javax Persistence?
-
¿Qué hace la anotación @Entity?
-
¿Cuál es la diferencia entre create, update y validate para ddl-auto?
-
¿Cómo genera Spring Data JPA la implementación del repository?
-
¿Qué hace la anotación @GeneratedValue(strategy = GenerationType.IDENTITY)?
-
¿Por qué debe haber un constructor vacío en una entidad JPA?
-
¿Qué genera la anotación @Data de Lombok?
-
¿Qué configuración usar en producción para ddl-auto?
Entregables
A entregar:
- Código fuente completo con PostgreSQL funcional
- Capturas de pantalla mostrando:
- La aplicación en ejecución
- Los datos en pgAdmin
- Las consultas SQL en la consola
- Al menos 2 ejercicios completados
- Documento de respuestas a las preguntas de repaso
Criterios de Evaluación
| Criterio | Puntos |
|---|---|
| Integración PostgreSQL funcional | 25% |
| Entidad Greeting correctamente anotada | 20% |
| Repository creado y utilizado | 15% |
| Datos persistidos en PostgreSQL | 20% |
| Ejercicios completados | 10% |
| Preguntas de repaso respondidas | 10% |
| Total | 100% |
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