Actividad 5 - Spring Data JPA y Spring Data REST
El objetivo de esta actividad es desarrollar servicios REST utilizando Spring Data JPA y Spring Data REST con una base de datos PostgreSQL. Se implementarán dos proyectos: uno con una API REST personalizada y otro utilizando Spring Data REST para generar endpoints automáticamente.
1. Base de datos
Section titled “1. Base de datos”1.1. Docker Compose
Section titled “1.1. Docker Compose”Se utiliza Docker Compose para levantar el servicio de PostgreSQL:
services: postgres: image: postgres:18.3-alpine3.23 environment: POSTGRES_DB: dwsc POSTGRES_USER: estudiante POSTGRES_PASSWORD: estudiante ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes: postgres_data:El servicio de PostgreSQL se configura con:
- Imagen:
postgres:18.3-alpine3.23 - Base de datos:
dwsc - Usuario:
estudiante - Contraseña:
estudiante - Puerto:
5432:5432
1.2. Esquema de datos
Section titled “1.2. Esquema de datos”El esquema de datos se define en el script init.sql:
CREATE TABLE IF NOT EXISTS public.student ( id bigint NOT NULL GENERATED ALWAYS AS IDENTITY (INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1), name text COLLATE pg_catalog."default", surnames text COLLATE pg_catalog."default", dni character varying(255) COLLATE pg_catalog."default", CONSTRAINT student_pkey PRIMARY KEY (id)) TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.student OWNER TO estudiante;
CREATE TABLE IF NOT EXISTS public.degree ( id bigint NOT NULL GENERATED ALWAYS AS IDENTITY (INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1), code text COLLATE pg_catalog."default", name text COLLATE pg_catalog."default", programme text COLLATE pg_catalog."default", CONSTRAINT degree_pkey PRIMARY KEY (id)) TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.degree OWNER TO estudiante;
CREATE TABLE IF NOT EXISTS public.student_degree ( student_id bigint NOT NULL, degree_id bigint NOT NULL, CONSTRAINT student_degree_pkey PRIMARY KEY (student_id, degree_id), CONSTRAINT student_degree_ibfk_1 FOREIGN KEY (student_id) REFERENCES public.student (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT student_degree_ibfk_2 FOREIGN KEY (degree_id) REFERENCES public.degree (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION) TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.student_degree OWNER TO estudiante;
INSERT INTO public.student (dni, name, surnames) VALUES ('12345678V', 'Carlos', 'López Jiménez'), ('12345678G', 'César', 'González García');
INSERT INTO public.degree (code, name, programme) VALUES ('GRAD04015', 'Grado en Ingeniería Informática', 'Plan 2015'), ('MASTER7109', 'Máster en Tecnologías y Aplicaciones en Ingeniería Informática', NULL);
INSERT INTO public.student_degree (student_id, degree_id) SELECT s.id, d.id FROM public.student s, public.degree d WHERE s.dni = '12345678V' AND d.code = 'GRAD04015';INSERT INTO public.student_degree (student_id, degree_id) SELECT s.id, d.id FROM public.student s, public.degree d WHERE s.dni = '12345678G' AND d.code = 'MASTER7109';Se crean tres tablas:
- student: Alumnos con id, dni, name y surnames
- degree: Titulaciones con id, code, name y programme
- student_degree: Tabla relación Many-to-Many entre estudiantes y titulaciones
1.3. Configuración de Hibernate
Section titled “1.3. Configuración de Hibernate”La aplicación utiliza Hibernate con la propiedad ddl-auto=update para crear
automáticamente las tablas a partir de las entidades JPA:
spring.jpa.hibernate.ddl-auto=update2. Actividad 5 - JPA (API REST personalizada)
Section titled “2. Actividad 5 - JPA (API REST personalizada)”El primer proyecto utiliza Spring Data JPA con controladores REST personalizados.
2.1. Dependencias Maven
Section titled “2.1. Dependencias Maven”<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>4.0.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>es.ual.dwsc</groupId> <artifactId>activididad5-jpa</artifactId> <version>0.0.1-SNAPSHOT</version> <name>actividad5-jpa</name> <description>Demo project for Spring Boot</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc</artifactId> </dependency>
<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
</project>2.2. Entidades JPA
Section titled “2.2. Entidades JPA”Se crean las entidades Student y Degree con una relación Many-to-Many:
package es.ual.dwsc.actividad5.domain;
import java.util.Set;
import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import jakarta.persistence.JoinColumn;import jakarta.persistence.JoinTable;import jakarta.persistence.ManyToMany;import lombok.Getter;import lombok.Setter;
@Entity@Getter@Setterpublic class Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String dni; private String name; private String surnames;
@ManyToMany @JoinTable(name = "student_degree", joinColumns = { @JoinColumn(name = "student_id") }, inverseJoinColumns = { @JoinColumn(name = "degree_id") }) private Set<Degree> degrees;
}package es.ual.dwsc.actividad5.domain;
import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import lombok.Getter;import lombok.Setter;
@Entity@Getter@Setterpublic class Degree {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String code; private String name; private String programme;}2.3. Repositorios
Section titled “2.3. Repositorios”Se utilizan interfaces CrudRepository con métodos de consulta personalizados:
package es.ual.dwsc.actividad5.repository;
import org.springframework.data.repository.CrudRepository;
import es.ual.dwsc.actividad5.domain.Student;
public interface StudentRepository extends CrudRepository<Student, Long> {
Student findByName(String name);
Student findByDni(String dni);}package es.ual.dwsc.actividad5.repository;
import org.springframework.data.repository.CrudRepository;
import es.ual.dwsc.actividad5.domain.Degree;
public interface DegreeRepository extends CrudRepository<Degree, Long> {
Degree findByCode(String code);}2.4. Controladores REST
Section titled “2.4. Controladores REST”StudentController
Section titled “StudentController”package es.ual.dwsc.actividad5.controller;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import es.ual.dwsc.actividad5.domain.Student;import es.ual.dwsc.actividad5.repository.StudentRepository;
@RestController@RequestMapping("/students")public class StudentController {
@Autowired StudentRepository studentRepo;
@GetMapping public ResponseEntity<Iterable<Student>> getStudents() { return ResponseEntity.ok(studentRepo.findAll()); }
@GetMapping("/{name}") public ResponseEntity<Student> getStudentByName(@PathVariable String name) { Student student = studentRepo.findByName(name); if (student == null) { return ResponseEntity.notFound().build(); }
return ResponseEntity.ok(student); }
@GetMapping("/dni/{dni}") public ResponseEntity<Student> getStudentByDni(@PathVariable String dni) { Student student = studentRepo.findByDni(dni); if (student == null) { return ResponseEntity.notFound().build(); }
return ResponseEntity.ok(student); }
@PutMapping("/{dni}") public ResponseEntity<Student> updateStudent(@PathVariable String dni, @RequestBody Student student) { Student existing = studentRepo.findByDni(dni); if (existing == null) { return ResponseEntity.notFound().build(); } existing.setName(student.getName()); existing.setSurnames(student.getSurnames()); return ResponseEntity.ok(studentRepo.save(existing)); }
@DeleteMapping("/{dni}") public ResponseEntity<Void> deleteStudent(@PathVariable String dni) { Student student = studentRepo.findByDni(dni); if (student == null) { return ResponseEntity.notFound().build(); } studentRepo.delete(student); return ResponseEntity.noContent().build(); }
}DegreeController
Section titled “DegreeController”package es.ual.dwsc.actividad5.controller;
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import es.ual.dwsc.actividad5.domain.Degree;import es.ual.dwsc.actividad5.repository.DegreeRepository;
@RestController@RequestMapping("/degrees")public class DegreeController {
@Autowired DegreeRepository degreeRepo;
@GetMapping public ResponseEntity<Iterable<Degree>> getAllDegrees() { return ResponseEntity.ok(degreeRepo.findAll()); }
@GetMapping("/{code}") public ResponseEntity<Degree> getDegreeByCode(@PathVariable String code) { Degree degree = degreeRepo.findByCode(code); if (degree == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(degree); }
@PutMapping("/{code}") public ResponseEntity<Degree> updateDegree(@PathVariable String code, @RequestBody Degree degree) { Degree existing = degreeRepo.findByCode(code); if (existing == null) { return ResponseEntity.notFound().build(); } existing.setName(degree.getName()); existing.setProgramme(degree.getProgramme()); return ResponseEntity.ok(degreeRepo.save(existing)); }
@DeleteMapping("/{code}") public ResponseEntity<Void> deleteDegree(@PathVariable String code) { Degree degree = degreeRepo.findByCode(code); if (degree == null) { return ResponseEntity.notFound().build(); } degreeRepo.delete(degree); return ResponseEntity.noContent().build(); }
}2.5. Pruebas con curl
Section titled “2.5. Pruebas con curl”2.5.1. Students
Section titled “2.5.1. Students”Listar todos los estudiantes
curl -X GET http://localhost:8080/studentsRespuesta (200 OK):
[ { "id": 1, "dni": "12345678V", "name": "Carlos", "surnames": "López Jiménez", "degrees": [...] }, { "id": 2, "dni": "12345678G", "name": "César", "surnames": "González García", "degrees": [...] }]Buscar estudiante por nombre
curl -X GET http://localhost:8080/students/CarlosRespuesta (200 OK):
{ "id": 1, "dni": "12345678V", "name": "Carlos", "surnames": "López Jiménez", "degrees": [...]}Buscar estudiante por DNI
curl -X GET http://localhost:8080/students/dni/12345678VRespuesta (200 OK):
{ "id": 1, "dni": "12345678V", "name": "Carlos", "surnames": "López Jiménez", "degrees": [...]}Actualizar estudiante
curl -X PUT http://localhost:8080/students/12345678V \ -H "Content-Type: application/json" \ -d '{"name": "Carlos Antonio", "surnames": "López García"}'Respuesta (200 OK):
{ "id": 1, "dni": "12345678V", "name": "Carlos Antonio", "surnames": "López García", "degrees": [...]}Eliminar estudiante
curl -X DELETE http://localhost:8080/students/12345678VRespuesta (204 No Content):
Sin contenido en el cuerpo de la respuesta.
2.5.2. Degrees
Section titled “2.5.2. Degrees”Listar todas las titulaciones
curl -X GET http://localhost:8080/degreesRespuesta (200 OK):
[ { "id": 1, "code": "GRAD04015", "name": "Grado en Ingeniería Informática", "programme": "Plan 2015" }, { "id": 2, "code": "MASTER7109", "name": "Máster en Tecnologías y Aplicaciones en Ingeniería Informática", "programme": null }]Buscar titulación por código
curl -X GET http://localhost:8080/degrees/GRAD04015Respuesta (200 OK):
{ "id": 1, "code": "GRAD04015", "name": "Grado en Ingeniería Informática", "programme": "Plan 2015"}Actualizar titulación
curl -X PUT http://localhost:8080/degrees/GRAD04015 \ -H "Content-Type: application/json" \ -d '{"name": "Grado en Ingeniería Informática", "programme": "Plan 2020"}'Respuesta (200 OK):
{ "id": 1, "code": "GRAD04015", "name": "Grado en Ingeniería Informática", "programme": "Plan 2020"}Eliminar titulación
curl -X DELETE http://localhost:8080/degrees/GRAD04015Respuesta (204 No Content):
Sin contenido en el cuerpo de la respuesta.
3. Actividad 5 - REST (Spring Data REST)
Section titled “3. Actividad 5 - REST (Spring Data REST)”El segundo proyecto utiliza Spring Data REST para generar automáticamente endpoints RESTful a partir de los repositorios JPA.
3.1. Dependencias Maven
Section titled “3.1. Dependencias Maven”<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>4.0.5</version> <relativePath></relativePath> <!-- lookup parent from repository --> </parent> <groupId>es.ual.dwsc</groupId> <artifactId>actividad5-rest</artifactId> <version>0.0.1-SNAPSHOT</version> <name>actividad5-rest</name> <description>Demo project for Spring Boot</description> <url></url> <licenses> <license></license> </licenses> <developers> <developer></developer> </developers> <scm> <connection></connection> <developerConnection></developerConnection> <tag></tag> <url></url> </scm> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-hal-explorer</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-hal-explorer</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>3.2. Entidades JPA
Section titled “3.2. Entidades JPA”Las entidades son similares a las del proyecto JPA, pero con @JsonIgnore
para evitar referencias circulares en la relación Many-to-Many:
package es.ual.dwsc.actividad5_rest.domain;
import java.util.Set;
import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import jakarta.persistence.JoinColumn;import jakarta.persistence.JoinTable;import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.ManyToMany;import lombok.Getter;import lombok.Setter;
@Entity@Getter@Setterpublic class Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String dni; private String name; private String surnames;
@JsonIgnore @ManyToMany @JoinTable(name = "student_degree", joinColumns = { @JoinColumn(name = "student_id") }, inverseJoinColumns = { @JoinColumn(name = "degree_id") }) private Set<Degree> degrees;
}package es.ual.dwsc.actividad5_rest.domain;
import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import lombok.Getter;import lombok.Setter;
@Entity@Getter@Setterpublic class Degree {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
private String code; private String name; private String programme;}3.3. Repositorios Spring Data REST
Section titled “3.3. Repositorios Spring Data REST”Se utilizan interfaces que extienden PagingAndSortingRepository:
package es.ual.dwsc.actividad5_rest.repository;
import org.springframework.data.repository.CrudRepository;import org.springframework.data.repository.query.Param;import org.springframework.data.rest.core.annotation.RestResource;
import es.ual.dwsc.actividad5_rest.domain.Student;
@RestResource(path = "students", rel = "students")public interface StudentRepository extends CrudRepository<Student, Long> {
Student findByName(@Param("name") String name);
Student findByDni(@Param("dni") String dni);
}package es.ual.dwsc.actividad5_rest.repository;
import org.springframework.data.repository.CrudRepository;import org.springframework.data.rest.core.annotation.RestResource;
import es.ual.dwsc.actividad5_rest.domain.Degree;
@RestResource(path = "degrees", rel = "degrees")public interface DegreeRepository extends CrudRepository<Degree, Long> {}3.4. Endpoints generados
Section titled “3.4. Endpoints generados”Spring Data REST genera automáticamente los siguientes endpoints:
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /students | Listar todos los estudiantes (paginado) |
| GET | /students/{id} | Obtener estudiante por ID |
| POST | /students | Crear nuevo estudiante |
| PUT | /students/{id} | Actualizar estudiante |
| DELETE | /students/{id} | Eliminar estudiante |
| GET | /degrees | Listar todas las titulaciones |
| GET | /degrees/{id} | Obtener titulación por ID |
3.5. Pruebas con curl
Section titled “3.5. Pruebas con curl”3.5.1. Students
Section titled “3.5.1. Students”Listar estudiantes (HAL JSON)
curl -X GET http://localhost:8080/studentsRespuesta (200 OK):
{ "_embedded": { "students": [ { "id": 1, "dni": "12345678V", "name": "Carlos", "surnames": "López Jiménez" }, { "id": 2, "dni": "12345678G", "name": "César", "surnames": "González García" } ] }, "_links": { "self": { "href": "http://localhost:8080/students" }, "profile": { "href": "http://localhost:8080/profile/students" } }}Crear estudiante
curl -X POST http://localhost:8080/students \ -H "Content-Type: application/json" \ -d '{"dni": "12345678X", "name": "Ana", "surnames": "Martínez Sánchez"}'Respuesta (201 Created):
{ "id": 3, "dni": "12345678X", "name": "Ana", "surnames": "Martínez Sánchez", "_links": { "self": { "href": "http://localhost:8080/students/3" }, "student": { "href": "http://localhost:8080/students/3" }, "degrees": { "href": "http://localhost:8080/students/3/degrees" } }}Actualizar estudiante
curl -X PUT http://localhost:8080/students/3 \ -H "Content-Type: application/json" \ -d '{"dni": "12345678X", "name": "Ana María", "surnames": "Martínez Sánchez"}'Respuesta (200 OK):
{ "id": 3, "dni": "12345678X", "name": "Ana María", "surnames": "Martínez Sánchez", "_links": { "self": { "href": "http://localhost:8080/students/3" }, "student": { "href": "http://localhost:8080/students/3" }, "degrees": { "href": "http://localhost:8080/students/3/degrees" } }}Eliminar estudiante
curl -X DELETE http://localhost:8080/students/3Respuesta (204 No Content):
Sin contenido en el cuerpo de la respuesta.
3.5.2. Degrees
Section titled “3.5.2. Degrees”Listar titulaciones (HAL JSON)
curl -X GET http://localhost:8080/degreesRespuesta (200 OK):
{ "_embedded": { "degrees": [ { "id": 1, "code": "GRAD04015", "name": "Grado en Ingeniería Informática", "programme": "Plan 2015" }, { "id": 2, "code": "MASTER7109", "name": "Máster en Tecnologías y Aplicaciones en Ingeniería Informática", "programme": null } ] }, "_links": { "self": { "href": "http://localhost:8080/degrees" }, "profile": { "href": "http://localhost:8080/profile/degrees" } }}Crear titulación
curl -X POST http://localhost:8080/degrees \ -H "Content-Type: application/json" \ -d '{"code": "GRAD04020", "name": "Grado en Ingeniería del Software", "programme": "Plan 2020"}'Respuesta (201 Created):
{ "id": 3, "code": "GRAD04020", "name": "Grado en Ingeniería del Software", "programme": "Plan 2020", "_links": { "self": { "href": "http://localhost:8080/degrees/3" }, "degree": { "href": "http://localhost:8080/degrees/3" } }}Actualizar titulación
curl -X PUT http://localhost:8080/degrees/3 \ -H "Content-Type: application/json" \ -d '{"code": "GRAD04020", "name": "Grado en Ingeniería del Software", "programme": "Plan 2025"}'Respuesta (200 OK):
{ "id": 3, "code": "GRAD04020", "name": "Grado en Ingeniería del Software", "programme": "Plan 2025", "_links": { "self": { "href": "http://localhost:8080/degrees/3" }, "degree": { "href": "http://localhost:8080/degrees/3" } }}Eliminar titulación
curl -X DELETE http://localhost:8080/degrees/3Respuesta (204 No Content):
Sin contenido en el cuerpo de la respuesta.
4. Ejecución del proyecto
Section titled “4. Ejecución del proyecto”-
Iniciar la base de datos PostgreSQL
Se inicia el servicio de PostgreSQL con Docker Compose:
Terminal window docker-compose up -d -
Compilar y ejecutar actividad5-jpa
Terminal window cd actividad5-jpa./mvnw spring-boot:run -
Compilar y ejecutar actividad5-rest
Terminal window cd actividad5-rest./mvnw spring-boot:run -
Verificar los endpoints
- actividad5-jpa:
http://localhost:8080/students - actividad5-rest:
http://localhost:8080/students
- actividad5-jpa:
5. Ejercicios
Section titled “5. Ejercicios”5.1. La Base de Datos (El contenedor principal)
Section titled “5.1. La Base de Datos (El contenedor principal)”Si estás utilizando un motor de base de datos tradicional
(como MySQL, PostgreSQL, SQL Server, etc.), Spring Boot
necesita un lugar al cual conectarse. Por defecto, tienes
que crear la base de datos en tu gestor antes de arrancar
la aplicación (por ejemplo, ejecutando CREATE DATABASE mi_proyecto;).
Excepciones a esta regla:
-
El truco de MySQL: Si usas MySQL, puedes decirle al driver JDBC que cree la base de datos si no existe añadiendo un parámetro a tu URL en el archivo
application.properties:spring.datasource.url=jdbc:mysql://localhost:3306/mi_proyecto?createDatabaseIfNotExist=true -
Bases de datos en memoria (H2, HSQLDB): Si usas una base de datos en memoria para pruebas o desarrollo, no tienes que crear absolutamente nada. Spring Boot la levanta en la memoria RAM y crea todo automáticamente al iniciar.
5.2. ¿Tengo que crear cada tabla antes de poder acceder a ella?
Section titled “5.2. ¿Tengo que crear cada tabla antes de poder acceder a ella?”No, no es necesario crear las tablas manualmente. La propiedad de configuración
spring.jpa.hibernate.ddl-auto=update indica a Hibernate que cree y actualice
las tablas automáticamente a partir de las entidades JPA definidas en el código.
5.3. ¿Puedo crear e inicializar un esquema de datos desde mi aplicación cuando la ejecuto por primera vez (o cada vez que se ejecuta)?
Section titled “5.3. ¿Puedo crear e inicializar un esquema de datos desde mi aplicación cuando la ejecuto por primera vez (o cada vez que se ejecuta)?”Sí, es posible hacerlo de dos formas:
-
init.sql automático: El script
init.sqlse monta en el directorio/docker-entrypoint-initdb.d/del contenedor PostgreSQL, por lo que se ejecuta automáticamente la primera vez que se crea la base de datos. -
Hibernate con ddl-auto: La propiedad
ddl-autode Hibernate puede tomar diferentes valores:none: No se genera nadaupdate: Se actualiza el esquema existentecreate: Se crea el esquema eliminando el anteriorcreate-drop: Crea el esquema al iniciar y lo elimina al cerrar
Con
update, Hibernate crea las tablas automáticamente si no existen.