Construir una aplicación REST con Spring Boot y Base de Datos Oracle ejecutada desde Docker

Introducción 

En este artículo, vamos a crear una aplicación REST con Spring Boot conectada a una base de datos Oracle.  

Herramientas

  • Java 17
  • Spring Tool Suite (STS) o Intellij IDEA 
  • Oracle XE
  • Docker
  • Postman

Crear un proyecto Spring Boot

Vamos a ingresar a la siguiente url https://start.spring.io/, como se puede observar en la Figura #1.

Paso # 1:

  • Seleccionamos en el proyecto la opción Maven, el lenguaje Java, la versión de Spring Boot para este ejemplo 2.7.8

Paso # 2:

  • En el Project Metadata: Diligenciamos la información del proyecto, como el Group, Artifact, Nombre, si requiere de adicionar una descripción del proyecto, nombre del paquete con su respectivo empaquetamiento. Para esta demo en el Packaging seleccionamos Jar y en la versión de Java la número 17 que es la version LTS.  
Figura 1

Paso # 3:

Continuando con la gestión del información del proyectó adicionamos las siguientes dependencias.

  • Lombok: Es una librería java que contiene anotaciones que generan automáticamente métodos de ayuda después de la compilación como por ejemplo (setters, getters, constructores).
  • Spring Web: Proporciona características de integración comunes específicas de la web, para construir aplicaciones web, incluyendo RESTful, utilizando Spring MVC.

  • Spring Data JPA: Proporciona soporte de repositorio para Java Persistence API (JPA). Facilitando el desarrollo de aplicaciones que necesitan acceder a fuentes de datos JPA.

  • Spring Boot DevTools: Proporciona reinicio rápido de las aplicaciones.

  • Oracle Driver: Driver que proporciona acceso a Oracle.

  • Validator: Es el estándar para implementar la lógica de validación en el ecosistema Java, por ejemplo lo utilizamos en la clase validando los campos con sus respectivas restricciones. Por otro lado está bien integrado con Spring y Spring Boot.

  • Spring-boot-starter-parent: Todos los proyectos de spring boot construidos en maven utilizan spring boot starter parent en su pom.xml. Estas son algunas acciones que realiza :

    • Configurar la versión y las propiedades de Java.
    • Gestionar las dependencias y sus versiones.

Paso #4:

  • Hacemos click para generar el proyecto.

Revisamos la estructura del proyecto como se puede visualizar en la Figura #2, debería quedar algo así.

Figura 2

En este punto vamos a revisar el archivo pom.xml como se muestra en la Figura #3.

<?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>2.7.8</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.geovanny.code</groupId>
	<artifactId>demo-app-oracle</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo-app-oracle</name>
	<description>Demo project for Spring Boot with Oracle</description>
	<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-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</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-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<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>

En el siguiente paso vamos a crear los paquetes con sus respectivas clases e interfaces.

1. Entidad Estudiante

Creamos un paquete con el nombre model y dentro de el una Clase con el nombre Estudiante que contendrá toda la información básica sobre el estudiante, como la identificación, nombre, apellido, dirección, país, ciudad, edad.

package com.geovanny.code.model;
import lombok.NoArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Builder;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.persistence.GeneratedValue;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.GenerationType;
import javax.persistence.SequenceGenerator;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@EqualsAndHashCode(callSuper=false)
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Data
@Table(name="TBL_ESTUDIANTES")
public class Estudiante{
        @Id
        @Column(name = "ID")
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqEstudiantes")
        @SequenceGenerator(name = "seqEstudiantes", allocationSize = 1, sequenceName = "SEQ_ESTUDIANTES")
        @Builder.Default
        Long id=0L;
        @NotNull @NotBlank
        String nombres;
        @NotNull @NotBlank
        String apellidos;
        Integer edad;
        @NotBlank(message = "Direccion es requerida")
        @Size(min = 5, max = 50)
        String direccion;
        String ciudad;
        String pais;
}

2. Interfaz Repositorio JPA

Contendrá los métodos CRUD habituales y más para manejar las operaciones básicas. Por lo tanto, para crear nuestro repositorio personal, necesitamos extender el repositorio JPA de Spring Data. Permite que los servicios se comuniquen con la base de datos.

package com.geovanny.code.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.geovanny.code.model.Estudiante;
@Repository
public interface EstudianteRepositorio extends JpaRepository<Estudiante, Long> {
}

3. Servicio

La aplicación Spring Boot del lado del Backend utiliza Clases de Servicio para representan el núcleo de su aplicación. Para este ejemplo crearemos una interfaz donde crearemos todos los métodos para realizar las operaciones de la lógica de negocio. 

package com.geovanny.code.service;
import java.util.List;
import java.util.Optional;
import com.geovanny.code.model.Estudiante;
public interface EstudianteService {
	
    public List<Estudiante> findEstudianteAll();
    public Estudiante createEstudiante(Estudiante estudiante);
    public Estudiante updateEstudiante(Estudiante estudiante);
    public void deleteEstudiante(Long id);
    public Optional<Estudiante> getEstudiante(Long id);
}

3.1 Implementación del Servicio

En esta clase se implementa toda la lógica de negocio de nuestro ejemplo de estudiante, adicionamos la anotación @Service e implementamos la clase desde la interfaz de servicio.

package com.geovanny.code.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.geovanny.code.model.Estudiante;
import com.geovanny.code.repository.EstudianteRepositorio;
@Service
public class EstudianteServiceImpl implements EstudianteService{
	@Autowired
	private EstudianteRepositorio estudianteRepositorio;
	
	@Override
	public List<Estudiante> findEstudianteAll() {
		return estudianteRepositorio.findAll();
	}
	@Override
	public Estudiante createEstudiante(Estudiante estudiante) {
		return estudianteRepositorio.saveAndFlush(estudiante);
	}
	@Override
	public Estudiante updateEstudiante(Estudiante estudiante) {
		
		return estudianteRepositorio.save(estudiante);
	}
	@Override
	public void deleteEstudiante(Long id) {
		estudianteRepositorio.deleteById(id);
	}
	@Override
	public Optional<Estudiante> getEstudiante(Long id) {
		return estudianteRepositorio.findById(id);
	}
}

4. Excepción

Para este ejemplo solamente utilizaremos la excepción NOT_FOUND, queda para el lector si desea adicionar otra tipo de excepcion por ejemplo NO_CONTENT

package com.geovanny.code.exception;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ModeloNotFoundException extends RuntimeException {
	
	public ModeloNotFoundException(String mensaje)
	{
		super(mensaje);
	}
}

5. Controlador

Creamos el controlador que se utilizará para interactuar con los estudiantes y realizar las acciones CRUD. Permite que los Servicios se comuniquen con el Frontend.

package com.geovanny.code.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.PostMapping;
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 com.geovanny.code.exception.ModeloNotFoundException;
import com.geovanny.code.model.Estudiante;
import com.geovanny.code.service.EstudianteService;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
@RestController
@RequestMapping("/estudiantes")
public class EstudianteController {
	@Autowired
	private EstudianteService service;
	
	@GetMapping(produces = "application/json")
	public ResponseEntity<List<Estudiante>> listar() {
		List<Estudiante> estudiantes = new ArrayList<>();
		 estudiantes = service.findEstudianteAll();
		return new ResponseEntity<List<Estudiante>>(estudiantes, HttpStatus.OK);
	}
	
	@PostMapping
    public ResponseEntity<Estudiante> crearEstudiante(@Valid @RequestBody Estudiante estudiante){
        service.createEstudiante(estudiante);
        return new ResponseEntity<Estudiante>(HttpStatus.CREATED);
    }
	
	@GetMapping("/{id}")
	public ResponseEntity<Estudiante> getEstudianteById(@PathVariable("id") Long id){
		Estudiante estudiante= service.getEstudiante(id).orElseThrow(() -> new ModeloNotFoundException("Estudiante no encontrado $id"));
		return new ResponseEntity<Estudiante>(estudiante, HttpStatus.OK);
	}
	@DeleteMapping("/{id}")
	public ResponseEntity<Estudiante> deleteEstudiante(@PathVariable("id") Long id){
		service.deleteEstudiante(id);
		return ResponseEntity.ok().build();
	}
	@PutMapping("/{id}")
	public Estudiante updateEstudiante(@PathVariable("id") Long id, @Valid @RequestBody Estudiante estudiante){
		Estudiante dbestudiante =  service.getEstudiante(id).orElseThrow(() -> new ModeloNotFoundException("Estudiante No enocntrado"));
		dbestudiante.setNombres(estudiante.getNombres());
		dbestudiante.setApellidos(estudiante.getApellidos());
		dbestudiante.setDireccion(estudiante.getDireccion());
		dbestudiante.setEdad(estudiante.getEdad());
		dbestudiante.setCiudad(estudiante.getCiudad());
		dbestudiante.setPais(estudiante.getPais());
		return service.updateEstudiante(dbestudiante);
	}
		

6. Configuración de Application.yml

En este archivo .yaml se configura el puerto y la conexión a la base de datos de oracle.  

server:
  port: 8081
    
# Oracle #
spring:
  datasource:
    url: jdbc:oracle:thin:@localhost:1521:xe
    username: system
    password: oracle
    driverClassName: oracle.jdbc.driver.OracleDriver
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.Oracle12cDialect
    show-sql: true
    hibernate:
      ddl-auto: update

7. Creación de la imagen con Docker Compose

Creamos un archivo con el siguiente nombre dev-stack.yml y adicionaremos el siguiente código dentro del archivo.

version: '3.3'
services:
  oracle:
    image: truevoly/oracle-12c 
    container_name: oracledb    
    ports:    
      - 1521:1521
    environment:      
      - "ORACLE_PASSWORD=oracle"

Ejecutamos el siguiente comando en una terminal.

  • Docker compose -f dev-stack.yml up -d

8. Creación de la Base de Datos

Crearemos una carpeta en dentro de resource con el nombre script y adicionamos el siguiente código en sql que nos permite  crear una tabla en la base de datos. 

CREATE TABLE TBL_ESTUDIANTES(
    ID            NUMERIC(4) NOT NULL PRIMARY KEY,
    NOMBRES       VARCHAR2(60) NOT NULL UNIQUE,
    APELLIDOS     VARCHAR2(300) NOT NULL,
    DIRECCION	  VARCHAR2(150) NULL,
    EDAD          INTEGER NOT NULL,
    CIUDAD        VARCHAR2(50),
    PAIS          VARCHAR2(50),
   CONSTRAINT PK_TBL_ESTUDIANTES PRIMARY KEY (ID)
 );
CREATE SEQUENCE SEQ_ESTUDIANTES;

8.1 Insertar registro en la tabla de la base de datos

Esquema para insertar registros en la tabla de tbl_estudiantes.

INSERT INTO TBL_ESTUDIANTES (ID, NOMBRES, APELLIDOS, DIRECCION, EDAD, CIUDAD, PAIS) VALUES (SEQ_ESTUDIANTES.NEXTVAL, 'GEOVANNY', 'MENDOZA', 'Calle 41 San Jose',28,'Barranquilla', 'Colombia');
INSERT INTO TBL_ESTUDIANTES (ID, NOMBRES, APELLIDOS, DIRECCION, EDAD, CIUDAD, PAIS) VALUES (SEQ_ESTUDIANTES.NEXTVAL, 'OMAR', 'BERROTERAN', 'Bo grenada Casa C22',27,'Managua', 'Nicaragua');
COMMIT;

9. Gestión de Base de Datos

En esta sección exploraremos la gestión de la base de datos con el mismo intellij como se puede observar en la figura #4. 

Figura #4

9.1 Conectar a la interfaz de la base de Datos

Después seleccionar la Database adicionamos la conexión con nuestra base de datos en oracle como se muestra en la figura #5.  

Figura #5
  1. Seleccionamos el signo (+) y nos ubicamos donde dice Data Source.
  2. Seleccionamos la base de datos Oracle.  

9.2 Configuración el Data Sources y Drivers

En este paso ingresamos el user y password, para este ejemplo utilizamos system como user y el password oracle como se muestra en la figura #6. 

Figura #6

9.3 Ejecutar la aplicación

En este paso ejecutamos la aplicación para crear la tabla de estudiante como se muestra en la figura #7.

Nota: Tener el servicio de docker ejecutando 

Figura #7

Abrimos la console para realizar una consulta sobre la tabla tbl_estudiante como se muestra en la figura #8.

Figura #8

10. Prueba con Postman

Para este ejemplo abrimos postman e importamos el archivo de postman para configurar las pruebas. Esge archivo se encuentra en el proyecto de git y se encuentra en la siguiente url Blog.postman_collection.json

{
	"info": {
		"_postman_id": "23d8f83f-f55c-44fd-8411-aaf30b871850",
		"name": "Blog",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
	},
	"item": [
		{
			"name": "JugNicaragua",
			"item": [
				{
					"name": "GET - Estudiantes",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "http://localhost:8081/estudiantes",
							"protocol": "http",
							"host": [
								"localhost"
							],
							"port": "8081",
							"path": [
								"estudiantes"
							]
						}
					},
					"response": []
				},
				{
					"name": "POST - Estudiantes",
					"request": {
						"method": "POST",
						"header": [],
						"body": {
							"mode": "raw",
							"raw": "{\n    \"nombres\": \"Tzeitel\",\n    \"apellidos\": \"Zuleta\",\n    \"ciudad\": \"La Paz\",\n    \"direccion\": \"Calle 12 # 20-12\",\n    \"pais\": \"Colombia\",\n    \"edad\": 28 \n}\n",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "http://localhost:8081/estudiantes",
							"protocol": "http",
							"host": [
								"localhost"
							],
							"port": "8081",
							"path": [
								"estudiantes"
							]
						}
					},
					"response": []
				},
				{
					"name": "GET {id} - Estudiantes",
					"request": {
						"method": "GET",
						"header": [],
						"url": {
							"raw": "http://localhost:8081/estudiantes/1",
							"protocol": "http",
							"host": [
								"localhost"
							],
							"port": "8081",
							"path": [
								"estudiantes",
								"1"
							]
						}
					},
					"response": []
				},
				{
					"name": "DELETE - Estudiantes",
					"request": {
						"method": "DELETE",
						"header": [],
						"url": {
							"raw": "http://localhost:8081/estudiantes/3",
							"protocol": "http",
							"host": [
								"localhost"
							],
							"port": "8081",
							"path": [
								"estudiantes",
								"3"
							]
						}
					},
					"response": []
				},
				{
					"name": "PUT - Estudiantes",
					"request": {
						"method": "PUT",
						"header": [],
						"body": {
							"mode": "raw",
							"raw": "{\n    \"id\": 2,\n    \"nombres\": \"Omar\",\n    \"apellidos\": \"Berroteran Silva\",\n    \"ciudad\": \"Managua\",\n    \"direccion\": \"Bo grenada Casa C22\",\n    \"pais\": \"Nicaragua\",\n    \"edad\": 32 \n}\n",
							"options": {
								"raw": {
									"language": "json"
								}
							}
						},
						"url": {
							"raw": "http://localhost:8081/estudiantes/2",
							"protocol": "http",
							"host": [
								"localhost"
							],
							"port": "8081",
							"path": [
								"estudiantes",
								"2"
							]
						}
					},
					"response": []
				}
			]
		}
	]
}

11. Resultado de Postman 

En esta sección se visualiza las pruebas exitosas de postman con los métodos GET y POST como se muestran en la figura #9 y #10.

11.1 Prueba del Método GET: Acá se puede visualizar el resultado de la prueba del método GET donde nos muestra todo los estudiantes registrados, como se muestra en la figura #9     

Figura #9

11.2 Prueba del Método POST: Acá se puede visualizar la creación del estudiante con el método POST como se puede observar en la figura #10.

Conclusión

En este artículo hemos construido una aplicación REST con servicios básicos que se conectara a una base de datos Oracle, donde se crea una imagen en docker.

Repositorio

Referencias

  • Libros:
    • Full Stack Development with Spring Boot and React: Build modern and scalable web applications using the power of Java and React, 3rd Edition Aquí
    • Spring Boot in Practice, Somnath Musib Aquí
Nosotros y terceros seleccionados utilizamos cookies o tecnologías similares con fines técnicos y, con su consentimiento, para otras finalidades (“interacciones y funcionalidades básicas”, “mejora de la experiencia”, “medición” y “segmentación y publicidad”) según se especifica en la política de cookies. Usted es libre de otorgar, denegar o revocar su consentimiento en cualquier momento.    Configurar y más información
Privacidad