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í

Crear un archivo EAR usando MAVEN

En el STACK java existe un archivo EAR que no es mas que un archivo ZIP con una estructura especifica. EAR (Enterprise Application aRchive) /

Un EAR normalmente contiene 2 archivos contenedores mas, un WAR (Web Archive) y un EJB , un archivo que contiene los EJB, sin embargo puede contener muchos mas (técnicamente no tiene limites), incluyendo clientes de EJB, EJB locales, pero el mínimo de todo EAR es uno, un modulo o contenedor, el cual puede ser un WAR o un EJB.

Prerequisitos

Java 1.4, Maven 3.6.3

Creando el proyecto vía comando.

mvn archetype:generate -DgroupId=com.ejemplo.demo.ear -DartifactId=simple-ear -DarchetypeArtifactId=wildfly-javaee7-webapp-ear-archetype -DarchetypeGroupId=org.wildfly.archetype -DinteractiveMode=false

Usando el archetype “maven-archetype-quickstart”

$ mvn archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DgroupId=mtitek.maven.ear.samples \
-DartifactId=mtitek-maven-ear-samples \
-Dpackage=mtitek.maven.ear.samples \
-Dversion=1.0.0-SNAPSHOT \
-DinteractiveMode=false

Donde se usa un EAR

Actualmente dada la gran popularidad de Spring-Boot casi no se ven EAR, pero en el otro lado, fuera de spring-boot, en los servidores empresariales como: JBOSS, websphere, Apache TomEE, Wildfly.

Ejemplo de un proyecto EAR

Creando el EAR usando JetBrains IntelliJ IDEa

Usando el IDE para crear el proyecto gráficamente.

Creando un proyecto, paso uno.

Click en Crear.

Así queda el proyecto y puede eliminar a su gusto.

Referencias

  • https://en.wikipedia.org/wiki/EAR_(file_format)
  • https://maven.apache.org/plugins/maven-ear-plugin/

Generar una aplicación web con Maven.

¿Que voy a aprender?

En esta entrada te voy a mostrar como crear un cascaron para una aplicación Web monolítica en Java utilizando Maven.

En un post anterior te mostre como generar este mismo proyecto, pero con el IDE eclipse.

¿Que necesito tener instalado?

  1. Maven
  2. Java (No seremos exigentes con las versiones, pero preferiblemente de la 8 en adelante)
  3. Eclipse IDE para ver el resultado

Manos a la obra

Abrimos nuestra consola de preferenciate ubicas donde quieres que se genere el proyecto y ejecutas

mvn archetype:generate -DartifactId=webapp-sample -DgroupId=ni.org.jug -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Brevemente te explico que hace este comando

  1. archetype:generate : le indica al maven que queremos generar un proyecto apartir de una plantilla
  2. -DartifactId: Es el nombre de nuestro proyecto, esto es personalizable puedes llamarlo como tu quieras..
  3. -DgroupId: Es la paqueteria base, generalmente dado por el dominio de nuestra organización, esto es personalizable.
  4. -DarchetypeArtifactId: El nombre de la plantilla web de la cual queremos generar nuestro proyecto.
  5. -DinteractiveMode: Le indicamos que al generar nuestro proyecto no nos haga mas preguntas :D.

Una vez terminada la ejecución obtendremos el siguiente resultado:

Proyecto Generado.

Proyecto Generado.

Importar el proyecto a Eclipse

Una vez finalizado, vamos a importar el proyecto generado a nuestro eclipse IDE.

  1. Seleccionamos la opción File -> Import

import

Opción Import

2. Buscar la opción maven y seleccionar la opción Existing Maven Projects

Opción Maven

Opción Maven

3. Buscamos el directorio donde se encuentra el proyecto que generamos previamente, puedes utilizar el button Browse…

selección del proyecto

Selección del Proyecto

Resultado Final:

El resultado final es un proyecto web con una estructura generado a partir de una plantilla, ya con esto hemos hecho el primer paso para poder desarrollar un sistema web monolítico con Java

Proyecto Importado

Proyecto Importado

 

Persistencia de datos con Sqlite implementando JDBC.

Introducción

Hablar acerca de persistencia de datos con Sqlite, es considerar que la mayoría de aplicaciones requieren almacenar información. El objetivo puede ser la toma de decisiones, como es el caso de programas empresariales; o para mantener persistente una configuración de la misma aplicación. Podríamos requerir que esta información se almacene al lado del cliente o del servidor. En este artículo veremos cómo persistir nuestro datos del lado del cliente utilizando SQLite con Java.  Ahora que sabemos qué es SQLite y hemos dado nuestros primeros pasos con Maven, vamos a crear un pequeño proyecto en Java. A este proyecto lo nombraremos “jsqlite”. En él podremos hacer persistencia de datos con Sqlite implementando o utilizando JDBC puro.

El proyecto “jsqlite” estará desarrollado en Java y tendrá las siguientes características:

  • Portable
  • Lo podremos ejecutar desde una terminal
  • Creará la base de datos en el caso que no existiera.
  • Creará una tabla en el caso que no existiera.
  • También podremos agregar, listar, borrar y actualizar registros.
  • Está basado en patrones de diseño MVC y Singleton.

¿Qué es JDBC?

Java Database Connectivity es una API de Java con una colección de interfaces y clases. Nos ayuda a que los programas Java accedan a sistemas de gestión de bases de datos.
Las aplicaciones desarrolladas en Java, mediante las interfaces y clases estándar de JDBC, pueden hacer consultas escritas en el lenguaje de consulta estructurada (SQL). ¿Quieres saber más?

Desarrollo de jsqlite (Nuestro Proyecto).

Iniciamos el proyecto de persistencia de datos con Sqlite.

Iniciamos creando la estructura de directorios base. Utilizando Maven digitamos desde la terminal la siguiente instrucción:

mvn archetype:generate  -DgroupId=ni.jug -DartifactId=jsqlite -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Nos quedará la siguiente estructura de directorios:

. 
├── pom.xml 
└── src 
   ├── main 
   │   └── java 
   │       └── ni 
   │           └── jug 
   │               └── App.java 
   └── test 
       └── java 
           └── ni 
               └── jug 
                   └── AppTest.java

Crearemos las carpetas controller, view y model dentro de la carpeta main/java/ni/jug/

mkdir main/java/ni/jug/util
mkdir main/java/ni/jug/controller
mkdir main/java/ni/jug/view
mkdir main/java/ni/jug/model

Modificación el pom.xml

Modificamos el archivo pom.xml, agregamos la dependencia para el driver de SQLite. También agregamos el plugin maven-assembly-plugin para la construcción del jar y agregamos la propiedad para que al momento de compilar Maven utilice la versión de Java 11. Nos quedará de la siguiente manera:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>ni.jug</groupId>
    <artifactId>jsqlite</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jsqlite</name>
    <url>http://maven.apache.org</url>
    <dependencies>
    <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
      <dependency>
         <groupId>org.xerial</groupId>
         <artifactId>sqlite-jdbc</artifactId>
         <version>3.28.0</version>
      </dependency>
    </dependencies>
   <build>
     <plugins>
        <!-- Configuracion del plugin para contruccion del jar -->
        <plugin>
           <artifactId>maven-assembly-plugin</artifactId>
           <version>3.1.1</version>
           <configuration>
              <!-- Le decimos a maven que agrege en el manifiesto cual es la clase que contiene el metodo main -->
               <archive>
                  <manifest>
                     <addClasspath>true</addClasspath>
                     <mainClass>ni.jug.App</mainClass>
                  </manifest>
                </archive>
                <!-- Le decimos a maven que incluya las dependencia en el jar -->
                <descriptorRefs>
                     <descriptorRef>jar-with-dependencies</descriptorRef>
                 </descriptorRefs>
              </configuration>
             <executions>
                <execution>
                   <id>assemble-all</id>
                    <phase>package</phase> 
                    <goals>
                        <goal>single</goal>
                    </goals>
                 </execution>
              </executions>
            </plugin>
        </plugins>
   </build>
   <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>

Conexión con la base de datos.

Vamos a crear un clase con el nombre ConnectionBD y estará ubicada en main/java/ni/jug. Esta clase hará la conexión a la base de datos. Aunque SQLite soporta que múltiples procesos lean la base de datos, solo puede haber un proceso de escritura, por lo utilizaremos el patrón de diseño Singleton para evitar múltiples conexiones a la base de datos.

package ni.jug;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
 * Establece la conexion con la base de datos y nos devuelve la conexion por el metodo
 * @author gacs
 */
public class ConnectionBD {
    private static ConnectionBD connectionBD=null;    //inilicializamos la clase de conexion 
    private final String strUrl;                      //Almacena la direccion de la base de datos
    private final String strDriver ;                  //Almacena el driver de conexion
    private static Connection conn=null;
    private ConnectionBD() {
    	this.strDriver="org.sqlite.JDBC"; //Driver de conexion
    	strUrl="jdbc:sqlite:var/db/mybd.db"; //Parametros de conexion de conexion
    	setConnection(); //Establece la conexion y la almacena en la propiedad conn
	}
    public static Connection getConnection(){
           if(connectionBD==null)
        	   connectionBD=new ConnectionBD(); 
           return conn;
    }
    /**
     * metodo de conexion con la base de datos
     * Almacena la conexion en la propiedad conn
     */
    private void setConnection() {
        try {
            Class.forName(strDriver);
            conn=DriverManager.getConnection(strUrl);
        }catch(ClassNotFoundException | SQLException e){
               System.err.println(e);
        }		   
  }
}

Crearemos la carpeta ../var/db dentro la carpeta donde hemos creado el proyecto, al mismo nivel de la carpeta src y del archivo pom.xml. La estructura de directorio se verá así:

.
├── src
│   ├── main
│   │   └── java
│   │       └── ni
│   │           └── jug
│   │               ├── controller
│   │               ├── model
│   │               ├── util
│   │               └── view
│   └── test
│       └── java
│           └── ni
│               └── jug
└── var
    └── db

Persistiendo datos con el patrón de diseño MVC.

Modelo

Crearemos una clase que nos servirá de modelo. Esta clase tendrá el nombre de Persona y estará ubicada en la carpeta main/java/ni/jug/model. A continuación el código que contiene la clase Persona:

package ni.jug.model;
/**
 * Clase modelo Persona 
 * @author gacs
 *
 */
public class Persona {
	private int id;
	private String nombre;
	private String apellido;
	private String sexo;
    public Persona() {
	}
	public Persona(int id, String nombre, String apellido, String sexo) {
		this.id = id;
		this.nombre = nombre;
		this.apellido = apellido;
		this.sexo = sexo;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNombre() {
		return nombre;
	}
	public String getNombreCompleto() {
		return nombre+" "+apellido;
	}
	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
	public String getApellido() {
		return apellido;
	}
	public void setApellido(String apellido) {
		this.apellido = apellido;
	}
	public String getSexo() {
		return sexo;
	}
	public void setSexo(String sexo) {
		this.sexo = sexo;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Persona other = (Persona) obj;
		if (id != other.id)
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "[ "+id+"  "+  nombre +"  "+apellido + "  "+  sexo + "]";
	}
}

 

Vistas

Crearemos ahora las clases que nos darán la vista de los datos, como también las vistas que interactúan con el usuario. Estas clases estarán ubicadas en la carpeta main/java/ni/jug/view. Las clases que utilizaremos como vista son:

  • NewPersona: Esta clase se utiliza para que el usuario agregue un nuevo registro en la tabla persona.
  • ListPersona: Lista los registro de la tabla persona.
  • EditPersona: Con esta clase podremos editar y modificar un registro de la tabla persona.
  • DeletePersona: Con esta clase podremos borrar un registro almacenado en la tabla persona.

NewPersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
/**
 * Agrega registro a la tabla
 * @author gacs
 *
 */
public class NewPersona {
	private Persona pm; 
	public NewPersona(Persona pm) {
            this.pm = pm;
	}
	/**
	 * Este metodo es llamado por el controlador
	 */ 
    public void setRegister() {
        Scanner reg = Input.getInstance();
        System.out.println( "Nombre:" );
        pm.setNombre(reg.next());
        System.out.println( "Apellido:" );
        pm.setApellido(reg.next());
        System.out.println( "Sexo:" );
        pm.setSexo(reg.next());
    }
}

ListPersona.

package ni.jug.view;
import java.io.IOException;
import java.util.List;
import ni.jug.model.Persona;
/**
 * Lista los registro de la tabla
 * @author gacs
 *
 */
public class ListPersona {
    public void sayRegisters(List a) 
    {
        System.out.println(" Id "+"  -- Nombre --   "+" -- Apellido -- "+" - Sexo - ");
	a.forEach(p ->{
            System.out.println(p.toString());	
	});
        try
        {
            System.out.println("\n\n  Presione ENTER..");
            System.in.read();
	}
	catch(IOException exe)
	{
            System.out.printf("Error?");
	}
    }
	public void sayRegister(Persona a) 
	{
		System.out.println(" Id "+"  -- Nombre --   "+" -- Apellido -- "+" - Sexo - ");
		System.out.println(a.toString());	
	}
}

EditPersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
public class EditPersona {
	
    public int getRegister() {
        int register;
        Scanner r= Input.getInstance();
        System.out.print("Digite el Id del registro a modificar :");
        register=r.nextInt();
        return register;
    }	
    public void postRegister(Persona a) {
        Scanner r= Input.getInstance();
        System.out.println("Registro a modificar");
        System.out.println("Id      :"+a.getId());
        System.out.println("Apellido  :"+a.getApellido());
        a.setApellido(r.next());
        System.out.println("Nombre  :"+a.getNombre());
            a.setNombre(r.next());
        System.out.println("Sexo    :"+a.getSexo());
           a.setSexo(r.next());
    }
}

DeletePersona.

package ni.jug.view;
import java.util.Scanner;
import ni.jug.model.Persona;
import util.Input;
public class DeletePersona {
	
	public int getRegister() {
		int register;
		Scanner r= Input.getInstance();
		System.out.println("Digite el registro a eliminar");
		register=r.nextInt();
		return register;
	}
	public boolean postRegister(Persona a) {
		Scanner r= Input.getInstance();
		System.out.println("Se borra el registro");
		System.out.println("Id      :"+a.getId());
		System.out.println("Nombre  :"+a.getNombreCompleto());
		System.out.println("Sexo    :"+a.getSexo());
		System.out.print("Desear eliminar este registro ?[S/N]");
		String bln=r.next();
		return bln.equalsIgnoreCase("S");
	}
}

Clase util Input.

Vamos a crear una clase llama Input, la cual estará en la carpeta main/java/ni/jug/util . Esta clase devuelve la clase Scanner, la cual es utilizada por las vistas para interactuar con el usuario. El contenido de la clase es el siguiente:

package ni.jug.util;
import java.util.Scanner;
/**
 * Clase de utilidad para las vistas
 * aplicado bajo el modelo singleton
 * @author gacs
 */
public class Input {
	private static Scanner input=null;
	private Input() {
		input=new Scanner(System.in);
	}
	
	public static Scanner getInstance() {
        if(input==null)
     	   		new Input(); 
        return input;
	}
}

Controlador.

Ya tenemos el modelo y las vistas de de nuestro proyecto. Ahora vamos a crear nuestra clase controladora, la que nos hará la persistencia de los datos que suministró el usuario en las vista a través del modelo.  Esta clase tendrá el nombre de PersonaController y estará ubicado en la carpeta main/java/ni/jug/controller, y tendrá el siguiente código:

package ni.jug.controller;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import ni.jug.ConnectionBD;
import ni.jug.model.Persona;
import ni.jug.view.DeletePersona;
import ni.jug.view.EditPersona;
import ni.jug.view.ListPersona;
import ni.jug.view.NewPersona;
/**
 * Clase de control para la tabla persona
 * @author gacs
 */
 public class PersonaController {
	private final String SQLINSERT="INSERT INTO PERSONA VALUES(NULL,?,?,?)";
	private final String SQLQUERY="SELECT Id,Nombre,Apellido,Sexo FROM PERSONA";
	private final String SQLQUERYONE=SQLQUERY+" WHERE Id=?";
	private final String SQLDELETE="DELETE FROM PERSONA WHERE Id=?";
	private final String SQLUPDATE="UPDATE PERSONA SET Nombre=?, Apellido=?,Sexo=? WHERE Id=?";
	private Connection conn;
	public PersonaController() {
		initController();
	}
	/**
	 * Instancia la conexion, crea la tabla si esta no existe
	 */
	
	private void initController() {
		conn=ConnectionBD.getConnection();
		PreparedStatement pst=null;
		String sqlCreate="CREATE TABLE IF NOT EXISTS PERSONA (" + 
				"	Id	integer PRIMARY KEY autoincrement," + 
				"	Nombre	 varchar ( 25 ) NOT NULL," + 
				"	Apellido varchar ( 25 ) NOT NULL," + 
				"	Sexo char  NOT NULL" + 
				"	)";
		try {
			pst=conn.prepareStatement(sqlCreate);
			pst.execute();
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
	}
	
	/**
	 * llama a la vista NewPersona para que el usuario agregue los valores a los campos
	 * luego estos valores se agregan a la tabla
	 */
	public void addRegister() {
		Persona persona=new Persona();
		NewPersona view =new NewPersona(persona);
		view.setRegister();
		PreparedStatement pst=null;
		try {
			pst=conn.prepareStatement(SQLINSERT);
			pst.setString(1, persona.getNombre());
			pst.setString(2, persona.getApellido());
			pst.setString(3, persona.getSexo());
			pst.executeUpdate();
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
	}
/**
 * Extrae los registro de la tabla y los visualiza en pantalla por medio de la vista
 * ListPersona
 */
	public void getRegisters() {
		List personas=new ArrayList<>();
		PreparedStatement pst=null;
		ResultSet rs=null;
		try {
			pst=conn.prepareStatement(SQLQUERY);
			rs=pst.executeQuery();
			while(rs.next()) {
				personas.add(new Persona( rs.getInt("Id"),
						rs.getString("Nombre"),
						rs.getString("Apellido"),
						rs.getString("Sexo")
						));
			}
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {
	    	try {
	    		if(pst!=null)
	    			pst.close();
	    		if(rs!=null)
	    			rs.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
		ListPersona view =new ListPersona();
		view.sayRegisters(personas);
	}	
	
	private Persona getRegister(int a) {
		PreparedStatement pst=null;
		ResultSet rs=null;
		Persona persona=null;
		try {
			pst=conn.prepareStatement(SQLQUERYONE);
			pst.setInt(1, a);
			rs=pst.executeQuery();
			if(rs.next()) {
				persona=new Persona( rs.getInt("Id"),
					rs.getString("Nombre"),
					rs.getString("Apellido"),
					rs.getString("Sexo")
				);
			}
	    }catch( SQLException e){
	        System.err.println(e);
	    }finally {		//Nos aseguramos que se cierre el PreparedStatement y ResultSet al finalizar el metodo
	    	try {
	    		if(pst!=null)
	    			pst.close();
	    		if(rs!=null)
	    			rs.close();
		    }catch( SQLException e){
		        System.err.println(e);
		    }
	    }		   
		
		return persona;
	}
	
	/**
	 * Borra un registro
	 * llama a la vista DeletePersona 
	 * con el metodo get determina el registro que quiere eliminar de la tabla
	 * con el metodo post visualiza el registro a eliminar y pide confirmacion para borrar
	 * 
	 */
    public void deleteRegister() {
	DeletePersona dp=new DeletePersona();
	Persona persona=null;
        int reg;
	PreparedStatement pst=null;
        do {
                reg=dp.getRegister();
                persona=getRegister(reg);
                if(persona.getId()!=reg) {
                        System.out.println("Registro no encontrado!!!");
                }
        }while(persona.getId()!=reg);
	if(dp.postRegister(persona)) {
            try {
		pst=conn.prepareStatement(SQLDELETE);
		pst.setInt(1, persona.getId());
		pst.execute();
            }catch( SQLException e){
	        System.err.println(e);
	    }
            finally {		//Nos aseguramos que se cierre el PreparedStatement al finalizar el metodo
           	try {
                    if(pst!=null)
		    	pst.close();
		}catch( SQLException e){
		        System.err.println(e);
                }
            }		   
	}
    }
	
    public void editRegister() {
        EditPersona edit =new EditPersona();
        Persona persona=null;
        int reg;
        PreparedStatement pst=null;
        do {
                reg=edit.getRegister();
                persona=getRegister(reg);
                if(persona.getId()!=reg) {
                        System.out.println("Registro no encontrado!!!");
                }
        }while(persona.getId()!=reg);
        //Envia el registro para ser modificado 
        edit.postRegister(persona);
        try {
                pst=conn.prepareStatement(SQLUPDATE);
                pst.setString(1, persona.getNombre());
                pst.setString(2, persona.getApellido());
                pst.setString(3, persona.getSexo());
                pst.setInt(4, persona.getId());
                pst.executeUpdate();
        }catch( SQLException e){
            System.err.println(e);
        }
            //Nos aseguramos que se cierre el PreparedStatement al finalizar el metodo
        finally {
            try {
                   if(pst!=null)
                            pst.close();
                }catch( SQLException e){
                    System.err.println(e);
            }
        }		   
    }
}

Clase principal (main class)

Para terminar el desarrollo de nuestro proyecto vamos a modificar la clase App.java, la cual se encuentra en la carpeta main/java/ni/jug. Esta es la primera clase que se iniciará en nuestra aplicación, por lo que en su contenido tendrá el método main. El código de esta clase sea el siguiente:

package ni.jug;
import java.sql.SQLException;
import java.util.Scanner;
import ni.jug.controller.PersonaController;
import ni.jug.util.Input;
/**
 * class main
  * @author gacs
 */
public class App 
{
    public App() {
    	menus();
    }
    public static void main( String[] args )
    {
            App app = new App();
    	System.out.println("Termino");	
    }
	/**
	 * Metodo que proporciona el menu de la aplicacion
	 */
    private void menus() {
    	Scanner menu;
    	menu=Input.getInstance();
    	PersonaController pc = new PersonaController();
        byte op=0;
        do {
            System.out.println( "-- Persistencia con SQLite --" );
            System.out.println( "1. Ingresar" );
            System.out.println( "2. Listar" );
            System.out.println( "3. Borrar" );
            System.out.println( "4. Editar" );
            System.out.println( "0. Salir de la aplicacion" );
            op=menu.nextByte();
            switch (op) {
                case 0:
                    System.out.println( "Saliendo del sistema" );
                    break;
                case 1:
                    pc.addRegister();
                    break;
                case 2:
                    pc.getRegisters();	
                    break;
                case 3:
                    pc.deleteRegister();	
                    break;
                case 4:
                    pc.editRegister();	
                    break;
                default:
                   System.out.println( "Solo puede eligir una de las opciones del menu" );
                    break;
            }
        }while(op!=0);
        menu.close();
   	try {
            ConnectionBD.getConnection().close(); 
	 }catch( SQLException e){
	    System.err.println(e);
	 }		   
    }
}

Hasta aquí ya tenemos finalizado nuestro proyecto, cumpliendo lo básico para persistir nuestros datos en base de datos SQLite por medio de JDBC.

Compilación y ejecución.

Compilando el proyecto.

Para terminar con el desarrollo de nuestro pequeño proyecto de persistencia de datos en  SQLite, procederemos con la compilación. Para ello, nos trasladamos a la carpeta que contiene nuestro proyecto y ejecutamos la siguiente sentencia.

 mvn clean install

Una vez que finalice de compilar, Maven nos habrá creado una carpeta target y en ella unos archivos jar.

target/jsqlite-1.0-SNAPSHOT.jar
target/jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar

Ejecución de jsqlite.

Para ejecutar nuestro aplicación, desde una terminal correremos el archivo jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar, con el siguiente sentencia

java -jar target/jsqlite-1.0-SNAPSHOT-jar-with-dependencies.jar

Nos presentará las siguientes pantallas:

 

Ingresamos datos con la opción 1.

Listamos los datos que ingresamos con la opción 2.

Salimos de la aplicación y volvemos a ingresar y listamos los registro que contiene la base de datos para comprobar si está persistiendo nuestra jsqlite.

Buenos, finalizamos y hemos alcanzado nuestro objetivo.

¿Qué les parece? ¿Dejé por fuera algo? ¿No entendiste algo? Déjamelo en los comentarios..

Introducción a Maven, primeros pasos.

Introducción

Al desarrollar en java siempre tenemos varias tareas que realizar. Entre las primeras es crear una estructura de directorio para nuestras fuentes y el descargar librerías con las dependencia que utilizaremos en nuestro proyecto.

También tendremos la tarea de compilar nuestro proyecto y asignarle un lugar donde ubicar nuestro archivos compilados (jar, war, etc.).

Para las tareas antes mencionamos y otras que necesitamos al desarrollar proyectos en Java, tenemos la herramienta Apache Maven.

(más…)

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