15 abril, 2020

Crear un CRUD Java Web usando Spring Boot, JPA, Thymeleaf y Bootstrap en Eclipse (Maven) + Extra API REST

En este post aprenderemos a crear un CRUD Java Web usando Spring Boot, Thymeleaf, Bootstrap  y Maven, que es una herramienta de software para la gestión y construcción de proyectos.

¿Qué es Maven?

Normalmente cuando trabajamos con Java/JavaEE el uso de librerías es algo común como en cualquier otro lenguaje de programación. Sin embargo, una librería puede depender de otras librerías para funcionar de forma correcta. Por ende, su gestión se puede volver tedioso a la hora de buscar las librerías y ver que versión exacta debemos de elegir. Así pues necesitamos más información para gestionarlo todo de forma correcta  y para ellos existe MAVEN, que es una herramienta que nos va a permitir tener una gestión correcta de nuestra librerías, proyectos y dependencias. Dentro de un proyecto Maven el archivo pom.xml (Proyect Object Model) es el de mayor importancia ya que contiene toda la información del artefacto que usará nuestro proyecto. Un Artefacto puede verse como una librería con esteroides (aunque agrupa mas conceptos). Contiene las clases propias de la librería pero ademas incluye toda la información necesaria para su correcta gestión (grupo, versión, dependencias etc).

¿Qué es Spring Framework?

Es el framework más popular para Java empresarial, para crear código de alto rendimiento, liviano y reutilizable.

¿Qué es Spring Boot?

Spring Boot es una de las tecnologías dentro del mundo de Spring de las que más se está hablando últimamente. Spring Boot es una herramienta que nace con la finalidad de simplificar aun más el desarrollo de aplicaciones basadas en el ya popular framework Spring Core. Spring Boot busca que el desarrollador solo se centre en el desarrollo de la solución, olvidándose por completo de la compleja configuración que actualmente tiene Spring Core para poder funcionar.

¿Qué es JPA?

Java Persistence API es una especificación de persistencia para la plataforma JAVA que aplica el Mapeo Relacional Objeto  (ORM: Object-Relational Mapping) permitiendo interactuar con bases de datos relacionales sin perder las ventajas de la orientación a objetos. Esto permite construir  aplicaciones  completamente orientadas a objetos. Algunas de las implementaciones mas conocidas de JPA son: Hibernate, TopLink, OpenJPA, EclipseLink.

¿Qué es Thymeleaf?

Thymeleaf es una biblioteca Java que implementa un motor de plantillas. El objetivo principal de Thymeleaf es permitir la creación de plantillas de una manera elegante y un código bien formateado. Sus dialectos Standard y SpringStandard permiten crear potentes plantillas naturales que se pueden visualizar correctamente en los navegadores de Internet, por lo que también funcionan como prototipos estáticos. Thymeleaf también puede extenderse desarrollando tus propios dialectos.

¿Qué es Bootstrap?

Bootstrap es una biblioteca multiplataforma o conjunto de herramientas de código abierto para diseño de sitios y aplicaciones web. Contiene plantillas de diseño con tipografía, formularios, botones, cuadros, menús de navegación y otros elementos de diseño basado en HTML y CSS, así como extensiones de JavaScript adicionales. A diferencia de muchos frameworks web, solo se ocupa del desarrollo front-end.


PASOS

1. Crear un proyecto Spring Boot

Ingresamos a la url https://start.spring.io/ y creamos nuestro proyecto Spring Boot

Cuando hayamos terminado de ingresar los datos, seleccionamos la opción Generate para descargar el proyecto y poder importarlo desde Eclipse.

No debemos de olvidarnos en agregar las dependencias para este proyecto.


2. Importar el proyecto

Abrimos el Eclipse y seleccionamos la opción File/Import/. en la ventana que nos aparece seleccionamos la opción Existing Maven Projects.


Clic en siguiente y buscamos el directorio en donde tenemos descomprimido el proyecto que hemos generado anteriormente y le damos clic en la opción finalizar. Esperamos unos segundos o minutos hasta que el proyecto termine de importarse correctamente.


Tendremos la siguiente estructura:


3. Configurar la base de datos

Configuramos nuestro application.properties que tendrá las propiedades de nuestra base de datos MySQL.


Clic en el archivo application.properties e ingresamos las siguientes propiedades:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#Data source
 
#Indica el driver/lib para conectar java a mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
#Url donde esta el servicio de tu mysql y el nombre de la base de datos
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
 
#Usuario y contrasena para tu base de datos descrita en la línea anterior
spring.datasource.username=root
spring.datasource.password=root
 
#[opcional]Imprime en tu consola las instrucciones hechas en tu base de datos.
spring.jpa.show-sql = true

Ahora creamos la base de datos:
1
2
3
4
5
6
7
8
9
10
11
12
13
Create database springboot_jpa_crud;
 
Use springboot_jpa_crud;
 
Create table persona
(
 id INT NOT NULL AUTO_INCREMENT,
 nombre VARCHAR(255) NULL,
 apellido VARCHAR(255) NULL,
 direccion VARCHAR(255) NULL,
 telefono VARCHAR(45) NULL,
 PRIMARY KEY(id)
);

Ejecutamos la clase principal SpringbootCrudApplication.java que nos creo por defecto al momento de generar el proyecto. Para ello, hacemos clic derecho a la clase y seleccionamos la opción Run As/ Java Application. Esta clase principal se encargará de publicar nuestra aplicación Spring Boot e iniciará Tomcat por nosotros.



4. Crear el modelo


Crear la clase Persona.java dentro del paquete com.aprendec.model.

⧭ ORM es una técnica para convertir datos entre el sistema utilizado en lenguaje de POO y el utilizado en una Base de Datos Relacional. Como resultado de aplicar esta técnica, el programador podrá codificar en Java como si interactuará virtualmente con una Base de Datos Orientada a Objetos, cuya base es una Base de Datos Relacional, permitiendo así aprovechar todas las características de la programación Orientada a Objetos al interactuar con una Base de Datos Relacional.

⧭ Una instancia de una clase Entidad representará una fila de una Tabla relacional.

⧭ En el caso de JPA, tenemos 2 posibilidades de aplicar ORM
  1. Utilizando el archivo de configuración orm.xml 
  2. Utilizando @Anotaciones 
En el post nosotros aplicaremos @Anotaciones.  El uso de las anotaciones requiere que se importe el paquete “javax.persistence.*” dentro de la clase Java que representa a la Entidad.

Aplicando ORM a la Clase “Empleado”.

@Entity: Significa que esta clase entidad representa una tabla de datos relacional
@Table: es la tabla que representa esta clase entidad.
@id: sirve para indicar el atributo que representa la PK de la tabla.
@column: sirve para indicar que el atributo es una columna, en este caso no es necesario ya que los atributos de la clase tienen el mismo nombre que las columnas de la tabla relacional.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.aprendec.model;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
 
@Entity
public class Persona {
 
 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;
 
 @Column
 private String nombre;
 
 @Column
 private String apellido;
 
 @Column
 private String direccion;
 
 @Column
 private String telefono;
 
 public Long getId() {
  return id;
 }
 
 public void setId(Long id) {
  this.id = id;
 }
 
 public String getNombre() {
  return nombre;
 }
 
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 
 public String getApellido() {
  return apellido;
 }
 
 public void setApellido(String apellido) {
  this.apellido = apellido;
 }
 
 public String getDireccion() {
  return direccion;
 }
 
 public void setDireccion(String direccion) {
  this.direccion = direccion;
 }
 
 public String getTelefono() {
  return telefono;
 }
 
 public void setTelefono(String telefono) {
  this.telefono = telefono;
 }
}

5. Crear el DAO

Crear la interfaz PersonaDAO.java dentro del paquete com.aprendec.dao. Esta interfaz debe extender de CrudRepository y debemos de pasarle el objeto Persona y el tipo de Long de la llave primaria.



Con esto ya se nos ha generado los métodos básico del CRUD en un repositorio.

1
2
3
4
5
6
7
8
9
package com.aprendec.dao;
 
import org.springframework.data.repository.CrudRepository;
 
import com.aprendec.model.Persona;
 
public interface PersonaDAO extends CrudRepository<Persona, Long> {
 
}

6. Crear el servicio y su implementación

Creamos la interfaz PersonaService.java dentro del paquete com.aprendec.service.

Lo clásico que iria en un servicio seria colocar los métodos para guardar, modificar, eliminar, buscar y listar. Sin embargo, esto podría ser hasta repetitivo considerando la cantidad de tablas u entidades, lo ideal sería reutilizar código usando clases genéricas las cuales puedan ser reutilizadas en cualquier servicio de una aplicación. Para ello, vamos a crear un paquete llamado com.aprendec.commons que contendrá nuestras clases genéricas.

Creamos la interface GenericService.java que contendrá los métodos CRUD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.aprendec.commons;
 
import java.io.Serializable;
import java.util.List;
 
public interface GenericService<T, ID extends Serializable> {
 
 T save(T entity);
 
 void delete(ID id);
 
 T get(ID id);
 
 List<t> getAll();
}

Creamos la clase GenericServiceImpl.java que implementará la interfaz GenericService.java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.aprendec.commons;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
 
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Service;
 
@Service
public abstract class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> {
 
 @Override
 public T save(T entity) {
  return getDao().save(entity);
 }
 
 @Override
 public void delete(ID id) {
  getDao().deleteById(id);
 }
 
 @Override
 public T get(ID id) {
  Optional<t> obj = getDao().findById(id);
  if (obj.isPresent()) {
   return obj.get();
  }
  return null;
 }
 
 @Override
 public List<t> getAll() {
  List<t> returnList = new ArrayList<>();
  getDao().findAll().forEach(obj -> returnList.add(obj));
  return returnList;
 }
 
 public abstract CrudRepository<T, ID> getDao();
 
}

Creamos la clase PersonaServiceImpl.java dentro del paquete com.aprendec.service.impl. que extenderá de la clase GenericServiceImpl.java e implementará la interfaz PersonaService.java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.aprendec.service.impl;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Service;
 
import com.aprendec.commons.GenericServiceImpl;
import com.aprendec.dao.PersonaDao;
import com.aprendec.model.Persona;
import com.aprendec.service.PersonaService;
 
@Service
public class PersonaServiceImpl extends GenericServiceImpl<Persona, Long> implements PersonaService {
 
 @Autowired
 private PersonaDao personaDao;
 
 @Override
 public CrudRepository<Persona, Long> getDao() {
  return personaDao;
 }
 
}

Ahora debemos hacer que la interfaz PersonaService.java extienda de la interfaz GenericService.java y le pasaremos como parámetros la clase Persona y el tipo de dato Long de la llave primaria.

1
2
3
4
5
6
7
8
package com.aprendec.service;
 
import com.aprendec.commons.GenericService;
import com.aprendec.model.Persona;
 
public interface PersonaService extends GenericService<Persona, Long>  {
  
}

7. Creamos el controlador

Creamos la clase PersonaController.java dentro del paquete com.aprendec.controller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.aprendec.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.RequestMapping;
 
import com.aprendec.model.Persona;
import com.aprendec.service.PersonaService;
 
@Controller
public class PersonaController {
 
 @Autowired
 private PersonaService personaService;
 
 @RequestMapping("/")
 public String index(Model model) {
  model.addAttribute("list", personaService.getAll());
  return "index";
 }
 
 @GetMapping("/save/{id}")
 public String showSave(@PathVariable("id") Long id, Model model) {
  if (id != null && id != 0) {
   model.addAttribute("persona", personaService.get(id));
  } else {
   model.addAttribute("persona", new Persona());
  }
  return "save";
 }
 
 @PostMapping("/save")
 public String save(Persona persona, Model model) {
  personaService.save(persona);
  return "redirect:/";
 }
 
 @GetMapping("/delete/{id}")
 public String delete(@PathVariable Long id, Model model) {
  personaService.delete(id);
 
  return "redirect:/";
 }
 
}

Estructura del proyecto hasta este punto.


8. Importar Bootstrap

Descargar la librería de bootstrap ingresando al siguiente enlace: https://getbootstrap.com/.



Luego de descargar y descomprimir el zip que nos descargó, debemos copiar las carpetas css y js a nuestro proyecto. Para ello, copiamos las carpetas dentro del folder static que esta ubicado dentro de src/main/resources/.


9. Crear la vista

Dentro de la carpeta templeates, creamos la vista index.html y save.html,

Debemos importar dentro del html el Thymeleaf, para ello, debemos agregar el siguiente namespace: http://www.thymeleaf.org

. index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>CRUD Springboot</title>
 
<!-- Bootstrap CSS -->
<link href="../static/css/bootstrap.css" rel="stylesheet"
 th:href="@{/css/bootstrap.css}">
</head>
<body>
 <div class="container">
  <br>
  <h2 th:if="${list.isEmpty()}">
No hay personas</h2>
<div th:if="${!list.isEmpty()}">
  <h2>
Mantenimiento de Personas</h2>
<br>
   <table class="table">
    <thead>
<tr>
      <th>Nombre</th>
      <th>Apellido</th>
      <th>Dirección</th>
      <th>Teléfono</th>
      <th>Editar</th>
      <th>Eliminar</th>
     </tr>
</thead>
    <tbody>
<tr th:each="persona : ${list}">
      <td th:text="${persona.nombre}">
      <td th:text="${persona.apellido}">
      <td th:text="${persona.direccion}">
      <td th:text="${persona.telefono}">
      <td><a class="btn btn-info" th:href="@{/save/{id}(id=${persona.id})}">Editar</a></td>
      <td><a class="btn btn-danger" th:href="@{/delete/{id}(id=${persona.id})}">Eliminar</a></td>
     </tr>
</tbody>
   </table>
</div>
<br>
  <div>
   <a href="/save/0" class="btn btn-primary">Agregar</a>
  </div>
</div>
</body>
</html>

. save.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Personas</title>
 
<!-- Bootstrap CSS -->
<link href="../static/css/bootstrap.css" rel="stylesheet"
 th:href="@{/css/bootstrap.css}">
</head>
<body>
 <div class="container">
  <br>
  <h2>
Agregar Persona</h2>
<form action="#" th:action="@{/save}" th:object="${persona}"
   method="post">
   <div class="form-group">
    <label for="nombre">Nombre</label> <input type="text"
     class="form-control" id="nombre" th:field="*{nombre}" name="nombre">
   </div>
<input type="hidden" th:field="*{id}" name="id" id="id">
   <div class="form-group">
    <label for="nombre">Apellido</label> <input type="text"
     class="form-control" id="apellido" th:field="*{apellido}"
     name="apellido">
   </div>
<div class="form-group">
    <label for="nombre">Dirección</label> <input type="text"
     class="form-control" id="direccion" th:field="*{direccion}"
     name="direccion">
   </div>
<div class="form-group">
    <label for="nombre">Teléfono</label> <input type="text"
     class="form-control" id="telefono" th:field="*{telefono}"
     name="telefono">
   </div>
<button type="submit" class="btn btn-success">Guardar</button>
  </form>
</div>
</body>
</html>
10. Probar funcionalidad

Clic derecho a la clase principal y seleccionamos la opción Run As/ Java Application. Esta clase se encargará de publicar nuestra aplicación Spring Boot e iniciará Tomcat por nosotros. 

En el navegador ingresamos la url: localhost:48080. En mi ejemplo, el puerto que estoy utilizando es el puerto 48080, pero ustedes pueden cambiarlo si tienen alguna aplicación que ya esté usando ese puerto o pueden eliminarlo del archivo application.properties para que esté en el puerto por defecto que es el puerto 8080.


Extra

Si quisiéramos reutilizar la lógica creada anteriormente en un API Rest para que se pueda acceder a la base de datos desde una aplicación de escritorio, móvil o desde otra web. Tendríamos que crear un nuevo controlador y consumir los métodos antes creados.

Para reutilizar la lógica del controlador MVC, creamos la clase PersonaRestController.java dentro del paquete com.aprendec.controller. Esta clase será un controlador REST.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.aprendec.springbootcrud.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.RequestMapping;
 
import com.aprendec.springbootcrud.model.Persona;
import com.aprendec.springbootcrud.service.PersonaService;
 
@Controller
public class PersonaController {
 
 @Autowired(required=true)
 private PersonaService personaService;
 
 @RequestMapping("/")
 public String index(Model model) {
  model.addAttribute("list", personaService.getAll());
  return "index";
 }
 
 @GetMapping("/save/{id}")
 public String showSave(@PathVariable("id") Long id, Model model) {
  if (id != null && id != 0) {
   model.addAttribute("persona", personaService.get(id));
  } else {
   model.addAttribute("persona", new Persona());
  }
  return "save";
 }
 
 @PostMapping("/save")
 public String save(Persona persona, Model model) {
  personaService.save(persona);
  return "redirect:/";
 }
 
 @GetMapping("/delete/{id}")
 public String delete(@PathVariable Long id, Model model) {
  personaService.delete(id);
 
  return "redirect:/";
 }
 
}


Para probar el servicio usaré Advance Rest Client, pero podemos utilizar cualquier cliente como Postman, SoapUI, etc.



Descargar archivo

3 comentarios:

  1. hola tengo un error al acceder a los datos el error me lo da en List.

    ResponderBorrar
  2. Hola José, estoy haciendo mi primer proyecto y estoy intentando descargarme el proyecto del enlace pero no funciona. Podrías informarme de cómo poder obtener este proyecto de ejemplo para poder trabajar con él?
    Un saludo y gracias.

    ResponderBorrar
  3. hola, intento todo pero no me salen datos en la vista.. alguna sugerenccia? como podria probar hasta donde estan llegando losdatos

    ResponderBorrar