Introducción a Spring Boot: Aplicación Web con Spring Data JPA

Última actualización: 22/07/2018

logo springEn el tutorial Persistencia en BD con Spring: Integrando JPA, c3p0, Hibernate y EHCache, vimos de forma práctica los pasos a seguir para configurar una aplicación Maven que integre Spring con JPA, utilizando Hibernate como implementación. No es difícil, pero resulta tedioso tener que replicar esta configuración cada vez que empecemos un proyecto nuevo y la configuración puede complicarse si por ejemplo vamos a desarrollar un proyecto web completo y necesitamos también utilizar otros módulos de Spring tales como MVC, Data, Security, etc.

Numerosos programadores y empresas recurren a la creación de plantillas genéricas, por ejemplo en la forma de arquetipos Maven, que eviten tener que invertir tiempo en la creación y configuración de los nuevos proyectos desde cero (aunque esto conlleva el problema adicional de tener que mantener actualizadas dichas plantillas). Otra opción es utilizar plantillas de terceros disponibles en GitHub como por ejemplo las indexadas en yeoman, o irnos directamente a frameworks basados en Spring como JHipster aunque aquí ya entramos en el terreno de los generadores de código.

Teniendo en cuanta esta problemática, allá por 2014 se publicó la primera versión estable de Spring Boot, un módulo que pretende solucionar estos problemas y acelerar el desarrollo de aplicaciones basadas en Spring gracias a una gestión automatizada de la infraestructura software que necesitamos en nuestra aplicación. En concreto: “Spring Boot proporciona una manera rápida de construir aplicaciones. Inspecciona el classpath y los beans que tengas configurado, hace asunciones razonables sobre lo que te falta y lo añade. Con Spring Boot puedes centrarte más en la lógica de negocio y menos en la infraestructura”.

Master Pyhton, Java, Scala or Ruby

Hay que tener presente que Spring Boot no es un framework al uso ni un generador de código, se centra en la configuración e integración de las dependencias que solemos necesitar de forma genérica para que sin ningún esfuerzo empecemos directamente a desarrollar nuestra aplicación. Incluso configura de forma embebida un servidor Tomcat o Jetty si así lo deseamos. Y lo mejor de todo es que no supone una limitación ya que podemos seguir realizando cualquier configuración de igual forma que si no tuviéramos Spring Boot.

En este tutorial daremos los primeros pasos para empezar a utilizar Spring Boot en nuestros proyectos. En concreto, crearemos una aplicación web con una simple pantalla que muestre un listado de datos obtenidos desde una tabla de una base de datos MySQL. Se asume que el lector tiene conocimientos, a nivel muy básico, de Maven, Spring Data JPA y Spring MVC.

Se usará Spring Boot 2 aunque se indicarán las diferencias más relevantes con respecto a Spring Boot 1.5 de las funcionalidades que veamos.

Creando un proyecto con Spring Boot para Spring Data JPA

Vamos a crear y configurar un proyecto Maven con Spring Boot análogo al de los tutoriales de Spring Data JPA. Como es habitual, empezamos configurando el pom.xml con las dependencias que necesitamos, y aquí viene la primera ventaja de adoptar Spring Boot: no definiremos como dependencias directamente los módulos de Spring necesarios (web, orm, security) sino una suerte de “metadependencias” (starters) de Spring Boot que incluyen todo lo necesario. Para ello añadimos lo siguiente al pom.xml:

  1. Utilizar Spring Boot como módulo padre de nuestro proyecto. Usaremos Spring Boot 1 para Spring 4 y Spring Boot 2 para Spring 5.
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.0.5.RELEASE</version>
    		<relativePath />
    	</parent>
    
    

    Nota: si no queremos o podemos utilizar Spring Boot como proyecto padre, consultar la configuración alternativa.

  2. Añadir todos los metamódulos (starters) de Spring Boot que encapsulen las funcionalidades que necesitemos. Los starters disponibles se pueden consultar directamente en GitHub en este enlace. En nuestro caso, para utilizar Spring Data JPA con Hibernate necesitaremos el siguiente:
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
  3. Adicionalmente también necesitamos el driver JDBC.
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    
  4. Opcionalmente se puede utilizar el plugin de Maven de Spring Boot que proporciona algunos “goals” interesantes para ejecutar y empaquetar nuestras aplicaciones.
    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-maven-plugin</artifactId>
    		</plugin>
    	</plugins>
    </build>
    

Spring Boot utiliza Logback, pero acostumbro a utilizar Log4j en mis proyectos. Para hacer este cambio, es necesario incluir el starter spring-boot-starter-log4j y excluir spring-boot-starter-logging de los otros starters de los que sea dependencia

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-log4j</artifactId>
  <version>1.3.8.RELEASE</version>
</dependency>

El pom del proyecto inicial (será modificado a medida que avancemos en el tutorial) queda así

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.danielme.demo</groupId>
	<artifactId>spring-boot</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>

	<name>spring-boot-demo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.5.RELEASE</version>
		<relativePath />
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j</artifactId>
			<version>1.3.8.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>


	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

Con el comando mvn dependency:list podemos comprobar todas las librerías que tenemos en nuestro proyecto.

Ahora tenemos que definir los parámetros de configuración de nuestra aplicación. Spring Boot los lee por defecto del fichero /src/main/resources/application.properties o /src/main/resources/application.yaml y en este listado encontramos los más habituales aunque no se incluyen los de todas las librerías con las Spring Boot se integra de forma automática.

Para nuestro ejemplo necesitamos como mínimo la url de conexión a la base de datos y las credenciales. Adicionalmente establecemos también el modo DDL de Hibernate, que por defecto es none, a update para que se sincronicen nuestras entidades JPA con las tablas de la base de datos (¡No usar esto en producción!).

spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=demo
spring.datasource.password=demo
spring.jpa.hibernate.ddl-auto=update

Vamos a añadir una entidad de JPA y su repositorio de JPA.

package com.danielme.springboot.entities;

import java.util.Calendar;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

@EntityListeners({ AuditingEntityListener.class })
@MappedSuperclass
public class AuditableEntity {
	@CreatedDate
	@Temporal(TemporalType.TIMESTAMP)
	private Calendar createdDate;

	@LastModifiedDate
	@Temporal(TemporalType.TIMESTAMP)
	private Calendar lastModifiedDate;

	@CreatedBy
	private String createBy;

	@LastModifiedBy
	private String lastModifiedBy;

	getter y setters...

package com.danielme.springboot.entities;

import java.util.Calendar;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "countries")
public class Country extends AuditableEntity
{
	 @Id
	 @GeneratedValue(strategy = GenerationType.IDENTITY)
	 private Long id;
	 
	 @Column(nullable = false, unique=true)
	 private String name;
	 
	 @Column(nullable = false)
	 private Integer population;
	 
	 @Column(updatable = false, nullable = false)
	 @Temporal(TemporalType.TIMESTAMP)
	 private Calendar creation;
	 
	 public Country()
	 {
		super();
	 }

	public Country(String name, Integer population)
	{
		super();
		this.name = name;
		this.population = population;
	}
	
	 @PrePersist
	 public void onPersist()
	 {
		 creation = Calendar.getInstance();
	 }
...getters y setters

package com.danielme.springboot.repositories;

import org.springframework.data.jpa.repository.JpaRepository;

import com.danielme.springboot.entities.Country;

public interface CountryRepository extends JpaRepository<Country, Long>{

}

Antes de continuar construyendo nuestra aplicación web, vamos a ver cómo ejecutar el proyecto utilizando una clase con una un método main. Para hacer que Spring Boot configure Spring anotamos esta la clase con @SpringBootApplication que aplica las funcionalidades proporcionadas por @Configuration, @EnableAutoConfiguration, @ComponentScan y @EnableWebMvc (esta última si tenemos Spring MVC en el classpath). En el método main lanzamos la aplicación de forma estática con el método run. Para ejecutar código en el arranque pero después de la iniciación de Spring, implementamos la interfaz CommandLineRunner.

package com.danielme.springboot;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.danielme.springboot.repositories.CountryRepository;

@SpringBootApplication
public class DemoApp implements CommandLineRunner {

	private static final Logger logger = Logger.getLogger(DemoApp.class);
	
	@Autowired
	CountryRepository countryRepository;
	
	public static void main(String[] args) {
		SpringApplication.run(DemoApp.class, args);
	}

	@Override
	public void run(String... arg0) throws Exception {
		logger.info(countryRepository.count());		
	}	

}

Ahora ejecutamos el método main como cualquier proyecto Maven y comprobamos el log.

También podemos utilizar el plugin de Maven de Spring Boot para empaquetar toda la aplicación en un jar ejecutable sin tener que realizar ninguna acción adicional utilizando el siguiente comando.

mvn package spring-boot:repackage

Este comando crea dos .jar en el directorio target.

spring-boot:repackage

El pequeño contiene sólo las clases propias del proyecto mientras que el grande incluye todas las dependencias y puede ser ejecutado mediante java -jar.

Si queremos ejecutar la aplicación “al vuelo”, es más rápido ejecutar simplemente

mvn spring-boot:run

Para más información sobre el empaquetado de aplicaciones consultar Maven: aplicaciones ejecutables.

Pool de conexiones

Spring Boot proporciona de serie una implementación de DataSource que ofrece un pool de conexiones. Esta implementación depende de la versión que utilicemos:

HikariCP es bastante más potente que el pool de Tomcat y en los benchmark suele ofrecer un mejor rendimiento, motivo por el cual el equipo de Spring lo ha adoptado. Sin embargo, HikariCP estaba soportado en Spring Boot 1 y podemos utilizarlo siguiendo estos dos pasos:

  1. Incluir la dependencia en el pom. La versión a utilizar la delegamos en Spring Boot.
    <dependency>
      <groupId>com.zaxxer</groupId>
      <artifactId>HikariCP</artifactId>
    </dependency>
    
  2. Añadir la siguiente línea al application.properties.
    spring.datasource.type: com.zaxxer.hikari.HikariDataSource
    
  3. En cualquier caso, los parámetros de HikariCP que queramos configurar, siempre con prudencia y en función de las necesidades de la aplicación, se definen en este mismo fichero con el prefijo spring.datasource.hikari, por ejemplo:

    spring.datasource.hikari.maximumPoolSize=20: 10
    

    Aplicación web con JSP

    Convirtamos nuestro ejemplo en una aplicación web con Spring MVC que muestre el listado de países que tengamos en la base de datos siguiendo los siguientes pasos.

    1. Añadimos el starter spring-boot-starter-web. Vamos a utilizar JSP para renderizar la vista de forma dinámica, pero si hiciéramos uso de alguno de los motores de plantillas compatibles con Spring Boot (Thymelef, FreeMarker, Mustache, Velocity, Groovy Templates) tendríamos que incluir el starter correspondiente, y en este caso ya no es necesario el spring-boot-starter-web.
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
    2. Al utilizarse JSP, necesitamos la dependencia para el lenguaje jstl.
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
      </dependency>
      
    3. Spring Boot utiliza un servidor Tomcat o Jetty embebido para desplegar la aplicación. Para utilizar Tomcat añadimos las siguientes dependencias.
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
      </dependency>
      		
      <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
      </dependency>
      
    4. Las herramientas de desarrollo de Spring Boot permiten, entre otras funcionalidades, la recarga automática de la aplicación web en el Tomcat al detectarse cambios en los ficheros incluidos en el classpath.
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
      </dependency>
      
    5. El valor del packaging debe ser war.

      <packaging>war</packaging>

    6. En el application.properties definimos el prefijo y sufijo que utilizará el ViewResolver de Spring MVC para encontrar los ficheros .jsp que renderizarán las respuestas de las peticiones web.
      spring.mvc.view.prefix= /WEB-INF/jsp/
      spring.mvc.view.suffix= .jsp
      

    Con estos sencillos pasos ya hemos convertido nuestra aplicación de ejemplo en una aplicación web. Vamos a desarrollar un controlador que atienda la raíz de la aplicación web para obtener los países de la base de datos utilizando el repositorio de Spring Data que ya tenemos. La lista de países se añade al Model de Spring MVC y un script JSP será el responsable de renderizar el html final que se enviará al navegador del usuario.

    package com.danielme.springboot.controllerstomcat;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.danielme.springboot.repositories.CountryRepository;
    
    @Controller
    public class CountryController {
    
    	@Autowired
    	CountryRepository countryRepository;
    
    	@RequestMapping("/")
    	public String list(Model model) {
    		model.addAttribute("countriesList", countryRepository.findAll());
    		return "countriesList";
    	}
    }
    

    Ubicamos countriesList en el directorio /src/main/WEB-INF/jsp.

    <%@ page contentType="text/html; charset=UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
    
    <!DOCTYPE html>
    <html>
    <head>
    <link rel="shortcut icon" type="image/png" href="/favicon.png">
    <title>Spring Boot</title>
    </head>
    <body>
    	
    <h1>Countries</h1>
    	
    <hr>
    	<c:choose>
    		<c:when test="${not empty countriesList}">
    			
                         <ul>
    		       <c:forEach var="item" items="${countriesList}">
    					
                              <li>${item.name}:<fmt:formatNumber value="${item.population}" /></li>
    
    			</c:forEach>
    		      </ul>
    
    		</c:when>
    		<c:otherwise>
    			<b>NO DATA</b>
    		</c:otherwise>
    	</c:choose>
    
    
    </body>
    </html>
    

    Los ficheros con recursos estáticos tales como imágenes, css, JavaScript, etc, se ubican por defecto en el directorio /src/main/resources/static, Son accesibles a partir de la url raíz de la aplicación, por ejemplo /src/main/resources/static/js/script.js estará en http://localhost:8080/js/script.js.

    Para arrancar la aplicación web tenemos varias opciones:

    • Crear un .war ejecutable con el comando que vimos antes (mvn package spring-boot:repackage).
    • Lanzar directamente la aplicación con mvn spring-boot:run.
    • Lanzar la aplicación, tanto en modo “normal” como en debug, en Eclipse utilizando el plugin de Spring instalable desde el Marketplace. También podemos descargar desde la web de Spring un Eclipse ya empaquetado con este plugin. En cualquier caso, en el menu contextual tanto del proyecto como de la clase con el Main tendremos la opción Spring Boot App dentro de Run As y Debug As.

      spring tool suite spring boot app

      Una vez lanzado el servidor, se puede detener desde la propia consola de Eclipse.

    La aplicación está disponible en la url http://localhost:8080. El puerto se puede cambiar con la propiedad server.port en el application.properties.

    Normalmente necesitaremos empaquetar las aplicaciones web en un war estándar que pueda ser desplegado directamente en un contenedor o servidor de aplicaciones como Tomcat. Conseguir esto es tan sencillo como hacer que la clase Main herede de SpringBootServletInitializer, clase que depende de la versión de Spring Boot:

    @SpringBootApplication
    public class DemoApp extends SpringBootServletInitializer
    {
    

    Con este pequeño cambio el war generado con Spring Boot sigue siendo autoejecutable con el Tomcat embebido pero ahora puede desplegarse en cualquier Tomcat o similar. Si queremos prescindir de la funcionalidad de autoejecución, hay que eliminar del pom las dependencias spring-boot-starter-tomcat y tomcat-embed-jasper. Esto provoca como efecto colateral que desde Eclipse la aplicación deba ser lanzada como cualquier proyecto web Maven estándar en un servidor y no con las herramientas del plugin de Spring.

    Spring initializr

    La creación inicial del proyecto se puede realizar de forma gráfica mediante esta herramienta vía web, o bien mediante un asistente en Eclipse gracias al plugin Spring Tool Suite del que ya hemos hablado. Para utilizarlo nos vamos a “File>New->Other” y seleccionamos el asistente Spring Boot->Spring Starter Project.

    El asistente consta de tres pantallas. En la segunda de ellas tenemos acceso a un catálogo categorizado con todos los starters.

    Los proyectos creados con esta herramienta incluyen un “wrapper” para Maven o Gradle, según la opción elegida, por lo que no es necesario tener instalado como prerrequisito Maven (o Gradle) para poder construir el proyecto. Además de esta forma nos aseguramos que el proyecto se gestione siempre con la versión de Maven o Gradle correcta. En mi opinión es una buena práctica utilizarlo aunque en el proyecto de ejemplo no lo he incluido por simplicidad.

    Disponemos de dos scripts, uno para Windows (cmd) y otro para Linux (bash), que invocaremos con los mismos parámetros que usaríamos si llamáramos directamente al ejecutable de Maven (mvn).

    Código de ejemplo

    El proyecto completo se encuentra disponible en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

    También se encuentran en GitHub los ejemplos oficiales de Spring Boot

    .

    Otros tutoriales relacionados con Spring

    Spring JDBC Template: simplificando el uso de SQL

    Persistencia en BD con Spring Data JPA

    Persistencia en BD con Spring: Integrando JPA, c3p0, Hibernate y EHCache

    Testing Spring con JUnit 4

    Ficheros .properties en Spring IoC

    Cursos aplicaciones móviles

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

A %d blogueros les gusta esto: