Spring MVC ... primer ejemplo (Rest)

Para este ejemplo crearemos una base de datos (yo uso postgresql) llamada proyecto, agregaremos una tabla usuario con los siguientes campos: id, nombre, password, logeado y area.

CREATE TABLE public.usuario
(
    id INTEGER NOT NULL,
    nombre CHARACTER VARYING NOT NULL,
    password CHARACTER VARYING  NOT NULL,
    logeado BOOLEAN,
    area CHARACTER VARYING,
    CONSTRAINT usuario_pkey PRIMARY KEY (id)
);

Ahora agregaremos unos datos:

INSERT INTO public.usuario(
        id, nombre, password, logeado, area)
        VALUES (1, 'Antony Yung', '00T$@l12', TRUE, 'sistemas'),(2, 'Adrian Uribe', '3EvBb00', FALSE, 'administracion'),
    (3,'Ernestina Pedroza','@22GgHQll',TRUE,'recursos humanos');

Una vez creado la base de datos y su correspondiente tabla nos enfocaremos a crear el ejemplo con Spring MVC.

¿Qués es lo que ncesitaremos?

  1. Archivo: build.gradle , para configurar las dependencias necesarias
  2. Archivo: application.properties, para configurar el acceso a la BD,entre otras cosas
  3. Dentro del paquete controller: UsuarioRestController.java y MainController.java
  4. Dentro del paquete config: WebSecurityConfig.java y MvcConfig.java
  5. Dentro del paquete entity: Usuario.java
  6. Dentro del paquete repository: UsuarioRepository.java
  7. Dentro del paquete service: UsuarioService.java y UsuarioServiceImpl.java
  8. Dentro de la subcarpeta src/main/resouces/templates: test_template.html , home.html , login.html y hello.html
  9. Dentro de la carpeta src/main/resources/static/js el archivo *.js: datatable.js (https://datatables.net/)

Comencemos

Este archivo nos servirá para configurar las dependencias necesarias pra nuestro proyecto (acceso a web, security, bases de datos, en este caso postgresql, etc.)

build.gradle

/**
*
*
*@description Ejemplo  
*@version 1.1.0
*
*
*
* 1. Construir proyecto: gradle build
* 2. Ver tareas disponibles: gradle task
* 3. Ejecutar: gradle run รณ gradle bootRun
*/

buildscript {

        ext {
                springBootVersion = '1.5.6.RELEASE'
        }

        repositories {
                mavenCentral()
                maven {
                        url "https://plugins.gradle.org/m2/"
                }
        }
        dependencies {
                classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
}

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
apply plugin: 'project-report'
apply plugin: 'org.springframework.boot'

version = '1.0.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8
mainClassName = "com.codemonkey.Application"
description ="""
Ejemplo de proyecto hecho con Gradle build
 
"""

jar {

        baseName='principal'

        manifest{
                attributes 'Main-Class': 'com.codemonkey.Application'
        }
}

repositories {
        mavenCentral()
}

dependencies {
        compile('org.springframework.boot:spring-boot-starter')
        compile('org.springframework.boot:spring-boot-starter-web')
                compile('org.springframework.boot:spring-boot-starter-actuator')
                compile('org.springframework.boot:spring-boot-starter-thymeleaf')
                providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
                testCompile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.25'
                compile("com.fasterxml.jackson.core:jackson-databind")
                compile("org.springframework.boot:spring-boot-starter-data-jpa")
                compile("org.springframework.boot:spring-boot-starter-data-rest")
                runtime('org.postgresql:postgresql')
                compile("org.springframework.boot:spring-boot-starter-security")
}

configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}

project.configurations.compile.resolvedConfiguration.resolvedArtifacts.each {
 println '   [Dependencias] '
 println 'artifact: '+it.name
 println 'referencia: '+it.file
 println '**************************'
}

Este archivo se usará para configurar el acceso a la base de datos en postgresql, indicar el uso de las plantillas Thymeleaf.
application.properties

#BANNER
banner.charset=UTF-8
banner.location=banner.txt

#THYMELEAF TEMPLATES
spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
#spring.thymeleaf.excluded-view-names= # Comma-separated list of view names that should be excluded from resolution.
#spring.thymeleaf.mode=HTML5 # Template mode to be applied to templates. See also StandardTemplateModeHandlers.
#spring.thymeleaf.prefix=classpath:/templates/ # Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.suffix=.html
#spring.thymeleaf.template-resolver-order= # Order of the template resolver in the chain.
#spring.thymeleaf.view-names= # Comma-separated list of view names that can be resolved.

#POSTGRESQL
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/proyecto
spring.datasource.username=postgres
spring.datasource.password=5432
#spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy

# JPA (JpaBaseConfiguration, HibernateJpaAutoConfiguration)
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

No debemos olvidar las clases principales del programa: Application.java y ServletInitializer.java
ServletInitializer.java

package com.codemonkey;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
                return application.sources(Application.class);
        }

}

Application.java

package com.codemonkey;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

        public static void main(String[] args) {
                SpringApplication.run(Application.class, args);
        }
}

Nuestro @Controller principal.
MainController.java.java

package com.codemonkey.controller;

import java.util.HashMap;
import java.util.Map;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@Controller
public class MainController {
       
      @RequestMapping(path="/", method=RequestMethod.GET)
        public String goHome(){
                return "index";
        }

}

El @RestController de la aplicación.
UsuarioRestController.java

package com.codemonkey.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import com.codemonkey.entity.Usuario;
import com.codemonkey.service.UsuarioService;

@RestController
@RequestMapping("/testUsuarios")
public class UsuarioRestController {
       
        @Autowired
        private UsuarioService usuarioService;
       
       
        //REST
    //http://localhost:8080/testUsuarios/usuarios
        @RequestMapping(path="/usuarios", method=RequestMethod.GET)
        public List<Usuario> getAllUsuarios(){
                return usuarioService.getAllUsuarios();
        }
       
       
        //http://localhost:8080/testUsuarios/usuario/1/
    @RequestMapping(value = "/usuario/{id}", method = RequestMethod.GET)
        public Usuario getUsuarioById(@PathVariable("id") long id){
                return usuarioService.getUsuarioById(id);
        }
       
       
        //http://localhost:8080/testUsuarios/testUsuarios
        @GetMapping("/testUsuarios")
    public ModelAndView test_template(){
        ModelAndView mav= new ModelAndView("test_template");
        return mav;
    }

}

El @Configuration de seguridad, donde indicamos el usuario y clave de acceso (root/123)
WebSecurityConfig.java

package com.codemonkey.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("root").password("123").roles("USER");
    }
}

El @Configuration de vistas home -> login -> hello.
MvcConfig.java

package com.codemonkey.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
   
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }

}

La clase Entidad que servirá para enlazar los datos de la tabla 'usuario'.

Usuario.java

package com.codemonkey.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Usuario {
        @Id
        @GeneratedValue
        @Column(name="id")
        private long id;
        @Column(name="nombre")
        private String nombre;
        @Column(name="password")
        private String password;
        @Column(name="logeado")
        private boolean logeado;
        @Column(name="area")
        private String area;
       
        public Usuario(){super();}
       
        public Usuario(String nombre, String password,
        boolean logeado, String area){
                super();
                this.nombre=nombre;
                this.password=password;
                this.logeado=logeado;
                this.area=area;
        }
       
        public void setId(long id){
                this.id=id;
        }
       
        public long getId(){
                return id;
        }
       
        public void setNombre(String nombre){
                this.nombre=nombre;
        }
       
        public String getNombre(){
                return nombre;
        }
       
        public void setPassword(String password){
                this.password=password;
        }
       
        public String getPassword(){
                return password;
        }
       
        public void setLogeado(boolean logeado){
                this.logeado=logeado;
        }
       
        public boolean isLogeado(){
                return logeado;
        }
       
        public void setArea(String area){
                this.area=area;
        }
       
        public String getArea(){
                return area;
        }
       
       
}

El @Repository, para hacer uso de los métodos necesarios para manipular los datos.
UsuarioRepository.java

package com.codemonkey.repository;

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

import com.codemonkey.entity.Usuario;

@Repository("usuarioRepository")
public interface UsuarioRepository extends JpaRepository<Usuario, Long>{

}

La interface que nos permite crear métodos para obtener los registros de la tabla 'usuario'.
UsuarioService.java

package com.codemonkey.service;

import java.util.List;

import com.codemonkey.entity.Usuario;

public interface UsuarioService {
       
        public List<Usuario> getAllUsuarios();
        public Usuario getUsuarioById(long id);
       
}

Esta clase nos permitirá descoplar el código.
UsuarioServiceImpl.java

package com.codemonkey.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.codemonkey.entity.Usuario;
import com.codemonkey.repository.UsuarioRepository;

@Service("usuarioService")
public class UsuarioServiceImpl implements UsuarioService {

        @Autowired
        private UsuarioRepository usuarioRepository;

        @Override
        public List<Usuario> getAllUsuarios() {
                return usuarioRepository.findAll();
        }

        @Override
        public Usuario getUsuarioById(long id) {
                return usuarioRepository.findOne(id);
        }

}

Ahora pasemos a los templates o vistas.

Página de bienvenida.
home.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Bienvenido</title>
        <style type="text/css">
           #centro {
    margin: auto;
    width: 50%;
    border: 0.8em solid #2f5777;
    padding: 10px;
}

h1{
    text-align: center;
}
        </style>
    </head>
    <body><div id="centro">
        <h1>Bienvenido</h1>
        <p>Da clic en el  <a th:href="@{/hello}">enlace</a> para entrar.</p></div>
    </body>
</html>

Página de login (root/123) para acceder la vista hello.
login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Formulario de entrada </title>
<style type="text/css">
   #centro {
    margin: auto;
    width: 50%;
    border: 0.8em solid #2f5777;
    padding: 10px;
}

#my_input{
    border: 1px solid #2f5777;
    font-size: medium;
    background-color: #2f5777;
    color: white;
}

h1{
    text-align: center;
}

</style>
    </head>
    <body>
        <div th:if="${param.error}" id="centro">
            Nombre de usuario y/o clave incorrectos.
        </div>
        <div th:if="${param.logout}" id="centro">
            Logeado.
        </div>
        <div id="centro">
            <h1>Formulario de entrada</h1>
        <form th:action="@{/login}" method="post">
            <div><label> Nombre de usuario : <input type="text" name="username"/> </label></div>
            <div><label> Clave de acceso: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Entrar" id="my_input"/></div>
        </form>
        </div>
       
    </body>
</html>

La página hello.html nos permitirá acceder a nuestro ejemplo.
hello.html

<!DOCTYPE html>
 <html lang="es">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <title>Has entrado</title>
        <script type="text/javascript" src="/js/funciones.js"></script>
        <link rel="stylesheet" type="text/css" media="all" href="css/estilos.css" th:href="@{css/estilos.css}" />

        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css"/>
    <script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js"></script>
    <script src="/js/datatable.js"></script>
        </head>
    <body>
    <div id="centro">
    <h1>Bienvenido</h1>
        <p th:inline="text">Hola [[${#httpServletRequest.remoteUser}]]!</p>
           <form th:action="@{/logout}" method="post">
            <input type="submit" value="Salir" id="my_input"/>
        </form> <br/>
        </div>
 <p>Da clic <a th:href="@{/testUsuarios/testUsuarios}">aquí</a> para entrar y ver los usuarios.</p>
    </body>
</html>

Esta página muestra una tabla con los datos de los usuarios registrados en la tabal 'usuario'.
test_template.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org">
<head>
        <meta charset="utf-8" />
        <title>Spring Boot + JPA + Datatables</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css"/>
        <script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js"></script>
        <script src="/js/datatable.js"></script>
</head>

<body>
        <h1>Usuarios</h1>
        <table id="usuariosTable" class="display">
       <thead>
            <tr>
                <th>Id</th>
                                <th>Nombre</th>
                <th>Password</th>
                <th>Logeado</th>
                                <th>Área</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <th>Id</th>
                                <th>Nombre</th>
                <th>Password</th>
                <th>Logeado</th>
                                <th>Área</th>
            </tr>
        </tfoot>
    </table>
   
</body>
</html>

Este archivo *.js nos permite recoger los datos del JSON creado y desplegarlo en una tabla html
datatable.js

$(document).ready( function () {
         var table = $('#usuariosTable').DataTable({
                        "sAjaxSource": "/testUsuarios/usuarios",
                        "sAjaxDataProp": "",
                        "order": [[ 0, "asc" ]],
                        "aoColumns": [
                            { "mData": "id"},
                      { "mData": "nombre" },
                                  { "mData": "password" },
                                  { "mData": "logeado" },
                                  { "mData": "area"},
                        ]
         })
});

Construir:
gradle build
Ejecutar:
gradle bootRun
Visualizar:
http://localhost:8080
Saldrá la página de bienvenida, debes dar clic en el enlace y aparecerá el formulario, introduce el usuario (root) y clave (123). Al acceder debes dar clic en el enlace y ver el resultado.

Resultado: