logo

Spring MVC - Hibernate

Introducción

Seguimos avanzando con Spring y vamos integrar a hibernate en un proyecto Spring MVC, para ello vamos realizar un CRUD, proyecto que podeis descargar de GitHub.

Para integrar hibernate con Spring MVC vamos a utilizar la clase LocalSessionFactoryBean que configura un objeto SessionFactory que puede pasarse a las clases DAO mediante la inyección de dependencias.

El Proyecto

La estructura del proyecto
estructura

Antes de seguir, recomiendo que os detengáis en el archivo database.properties para estudiarlo un poco. Todas las lineas están comentadas para una mayor compresión del mismo, en cualquier caso, al final del archivo dejo un par de links que me parecen interesantes y explican todo esto.

database.properties

# MySQL configuration
# ==================

# Indica el driver/lib para conectar java a mysql
mysql.driver=com.mysql.jdbc.Driver
# Url donde esta el servicio de tu mysql y el nombre de la base de datos
mysql.url=jdbc:mysql://localhost:3306/testdb
#Usuario para tu base de datos 
mysql.username=root
#Contrasena para tu base de datos 
mysql.password=

# Hibernate configuration
# ======================

# Esta propiedad hace que Hibernate genere el SQL optimizado para la base de datos elegida.
hibernate.dialect=org.hibernate.dialect.MySQLDialect
# Indica que debe mostrar el log de las consultas sql ejecutadas, bueno para depurar
hibernate.show_sql=true
# update the schema (para el desarrollo de la aplicación, no para producción)
hibernate.hbm2ddl.auto=update
# Formatea la sentencia SQL generada para que sea más legible. (Ocupa más espacio en la pantalla).
hibernate.format_sql=true

# C3P0 configuration
# ==================

# Especifica el número mínimo de conexiones que debe mantener en un momento dado. Por defecto, mantendrá al menos tres conexiones.
hibernate.c3p0.min_size=5
# Especifica el número máximo de conexiones que puede mantener en un momento dado. Por defecto, mantendrá un máximo de 15 conexiones.
hibernate.c3p0.max_size=20
# Especifica cuántas conexiones debería intentar adquirir si el grupo se queda sin conexiones disponibles. 
# Por defecto, intentará adquirir tres conexiones nuevas.
hibernate.c3p0.acquire_increment=5
# Especifica el número de segundos que se mantendrá una conexión no utilizada antes de ser descartada. 
# Por defecto, las conexiones nunca expirarán en el pool.
hibernate.c3p0.timeout=1800
# El número de declaraciones preparadas se almacenará en la memoria caché
hibernate.c3p0.max_statements=150
                    
Las dependencias
pom.xml

<dependencies>
  <!-- Spring -->
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.5.RELEASE</version>
  </dependency>
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
  </dependency>
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
  </dependency>
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
      <scope>test</scope>
  </dependency>
  <!-- Hibernate -->
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
  </dependency>
  <!-- hibernate-c3p0 -->
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-c3p0</artifactId>
      <version>${hibernate.version}</version>
  </dependency> 
  <!-- Hibernate Validator -->
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>${hibernate.version}</version>
  </dependency>
  <!-- mysql-connector-java -->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.15</version>
      <scope>runtime</scope>
  </dependency>
  <!-- lombok -->
  <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.16</version>
      <scope>provided</scope>
  </dependency>
  <!-- javax.servlet -->
  <dependency>   
      <groupId>javax.servlet</groupId>   
      <artifactId>javax.servlet-api</artifactId>    
      <version>3.1.0</version>
      <scope>provided</scope>   
  </dependency>
  <dependency>  
      <groupId>javax.servlet.jps</groupId> 
      <artifactId>javax.servlet.jsp-api</artifactId> 
      <version>2.3.1</version> 
      <scope>provided</scope> 
  </dependency>
  <!-- jstl -->
  <dependency>  
      <groupId>javax.servlet</groupId> 
      <artifactId>jstl</artifactId> 
      <version>1.2</version>  
  </dependency>
  <!-- junit -->
  <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
  </dependency>
</dependencies>
                    
La Configuración

Por las clases SpringWebAppInitializer y WebMvcConfig no nos detendremos porque son iguales al proyecto anterior, pero SpringappBusinessConfig si que sufre modoficaciones importantes.

SpringWebAppInitializer.java

 package com.wanchopi.spring.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * Init app
 * @author Wanchopi
 *
 */
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[] { SpringappBusinessConfig.class};
    }

    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[] { WebMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}                       
                    
WebMvcConfig.java

package com.wanchopi.spring.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;


/**
 * Configure Spring MVC
 * @author Wanchopi
 *
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.wanchopi.spring")
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Bean(name = "viewResolver")
    public InternalResourceViewResolver getViewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        
        return viewResolver;
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/public/**").addResourceLocations("/public/"); 
    }
    
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        //messageSource.setCacheSeconds(1); 
        return messageSource;
    }

}                        
                    
SpringappBusinessConfig.java

package com.wanchopi.spring.config;

import java.util.Properties;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * 
 * @author Wanchopi
 *
 */
@Configuration
@ComponentScan(basePackages = { "com.wanchopi.spring.*" })
@PropertySource( "classpath:database.properties" )
@EnableTransactionManagement
public class SpringappBusinessConfig {
    
    @Autowired
    private Environment env;

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] {
            "com.wanchopi.spring.entity"
        });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
    }
    
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("mysql.driver"));
        dataSource.setUrl(env.getProperty("mysql.url"));
        dataSource.setUsername(env.getProperty("mysql.username"));
        dataSource.setPassword(env.getProperty("mysql.password"));
        return dataSource;
    }
    
    private Properties hibernateProperties() {
        Properties properties = new Properties();
        // Setting Hibernate properties
        properties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", env.getRequiredProperty("hibernate.format_sql"));
        properties.put("hibernate.hbm2ddl.auto", env.getRequiredProperty("hibernate.hbm2ddl.auto"));
        // Setting C3P0 properties
        properties.put("hibernate.c3p0.min_size", env.getProperty("hibernate.c3p0.min_size"));
        properties.put("hibernate.c3p0.max_size", env.getProperty("hibernate.c3p0.max_size"));
        properties.put("hibernate.c3p0.acquire_increment", env.getProperty("hibernate.c3p0.acquire_increment"));
        properties.put("hibernate.c3p0.timeout", env.getProperty("hibernate.c3p0.timeout"));
        properties.put("hibernate.c3p0.max_statements", env.getProperty("hibernate.c3p0.max_statements"));
        return properties;
    }
    
    @Bean
    public HibernateTransactionManager getTransactionManager() {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(sessionFactory().getObject());
        return transactionManager;
    }
}
                    

Como puedes ver, estamos usando la anotación @PropertySource para leer las propiedades JDBC, Hibernate y C3P0 del archivo database.properties, y por otra parte tenemos que anotar la clase con la anotación @EnableTransactionManagement para habilitar la gestión de transacciones.

Como decía en la introducción, LocalSessionFactoryBean crea un objeto SessionFactory.

El Modelo
Employee.java

package com.wanchopi.spring.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;

import lombok.Getter;
import lombok.Setter;

/**
 * Employed entity
 * 
 * @author Wanchopi
 *
 */
@Entity
@Table(name = "employees")
public class Employee {
    
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Getter @Setter
    private long id;
    
    @Column(name = "first_name")
    @NotNull
    @Size(min = 2, max = 50)
    @Getter @Setter
    private String firstName;
    
    @Column(name = "last_name")
    @NotNull
    @Size(min = 2, max = 50)
    @Getter @Setter
    private String lastName;
    
    @Column(name = "email")
    @NotNull
    @Email
    @Getter @Setter
    private String email;

}
                    
El Controlador
EmployeeController.java

 package com.wanchopi.spring.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.wanchopi.spring.entity.Employee;
import com.wanchopi.spring.service.IEmployeeService;

/**
 * Controller
 * @author Wanchopi
 *
 */
@Controller
public class EmployeeController {
    
    @Autowired
    private IEmployeeService service;
    
    @InitBinder
    public void initBinder(WebDataBinder dataBinder) {
        StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
        dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
    }
    
    @RequestMapping("/")
    public String findAllEmployees(Model model) {
        List<Employee> employees = service.getAllEmployees();
        model.addAttribute("employees", employees);
        return "index";
    }
    
    @GetMapping("/form")
    public String showForm(Model model) {
        Employee employee = new Employee();
        model.addAttribute("employee", employee);
        return "employee-form";
    }
    
    @PostMapping("/save")
    public String saveEmployee(@Valid @ModelAttribute("employee") Employee employee,
            BindingResult theBindingResult) {
        if (theBindingResult.hasErrors()) {
            return "employee-form";
        } else {
            service.saveEmployee(employee);
            return "redirect:/";
        }
    }
    
    @GetMapping("/delete")
    public String deleteEmployee(@RequestParam("id") long id) {
        service.deleteEmployee(id);
        return "redirect:/";
    }
    
    @GetMapping("/update")
    public String updateEmployee(@RequestParam("id") long id, Model model) {
        Employee employee = service.getEmployee(id);
        model.addAttribute("employee", employee);
        return "employee-update-form";
    }

}                      
                    

Observamos la inyección de dependencia de una interface nueva, EmployeeService, donde están definidas las operaciones básicas.

El Servicio
IEmployeeService.java

package com.wanchopi.spring.service;

import java.util.List;

import com.wanchopi.spring.entity.Employee;

/**
 * Service
 * @author Wanchopi
 *
 */
public interface IEmployeeService {
    
    public List<Employee> getAllEmployees();
    public void saveEmployee(Employee e);
    public void deleteEmployee(long id);
    public Employee getEmployee(long id);

}                        
                    

Y la clase que implementa la interface que a su vez presenta una inyección de dependencia de una interface que a su vez es implementada por una clase que es la responsable de proveer las operaciones CRUD, esto es un repositorio.

EmployeeService.java

package com.wanchopi.spring.service;

import java.util.List;

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

import com.wanchopi.spring.dao.IEmployeeDAO;
import com.wanchopi.spring.entity.Employee;

/**
 * Implements the IEmployeeService interface
 * @author Wanchopi
 *
 */
@Service
public class EmployeeService implements IEmployeeService {
    
    @Autowired
    private IEmployeeDAO employeeDAO;

    @Override
    @Transactional
    public List<Employee> getAllEmployees() {
        return employeeDAO.getAllEmployees();
    }

    @Override
    @Transactional
    public void saveEmployee(Employee e) {
        employeeDAO.saveEmployee(e);
    }

    @Override
    @Transactional
    public void deleteEmployee(long id) {
        employeeDAO.deleteEmployee(id);
    }

    @Override
    @Transactional
    public Employee getEmployee(long id) {
        return employeeDAO.getEmployee(id);
    }

}                        
                    

Estamos utilizando la anotación @Transactional que se aplica a la capa de servicio para el soporte de transacciones. La anotación @Service se utiliza para anotar las clases de implementación de la capa de servicios.

Como en este tutorial no estamos utilizando Sring Data JPA para gestionar las operaciones de la base de datos, lo mas "cercano" a un repositorio es el patrón DAO.

El patrón DAO
IEmployeeDAO.java

package com.wanchopi.spring.dao;

import java.util.List;

import com.wanchopi.spring.entity.Employee;

/**
 * DAO Interface
 * @author Wanchopi
 *
 */
public interface IEmployeeDAO {
    
    public List<Employee> getAllEmployees();
    public void saveEmployee(Employee e);
    public void deleteEmployee(long id);
    public Employee getEmployee(long id);

}                    
EmployeeDAO.java

package com.wanchopi.spring.dao;

import java.util.List;

import javax.persistence.TypedQuery;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.wanchopi.spring.entity.Employee;

/**
 * Implements the IEmployeeDAO interface
 * @author Usuario
 *
 */
@Repository
public class EmployeeDAO implements IEmployeeDAO {
    
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List<Employee> getAllEmployees() {
        @SuppressWarnings("unchecked")
        TypedQuery<Employee> query = sessionFactory.getCurrentSession().createQuery("from Employee");
        return query.getResultList();
    }

    @Override
    public void saveEmployee(Employee e) {
        sessionFactory.getCurrentSession().saveOrUpdate(e);
    }

    @Override
    public void deleteEmployee(long id) {
        Employee employee = getEmployee(id);
        sessionFactory.getCurrentSession().delete(employee);
    }

    @Override
    public Employee getEmployee(long id) {
        Employee employee = sessionFactory.getCurrentSession().get(Employee.class, id);
        return employee;
    }

}
                    

Observamos que esta clase al anotamos con @Repository por lo dicho mas arriba.

Las Vistas
index.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>index</title>
<link rel="stylesheet" href="public/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css">
<link rel="stylesheet" href="public/css/mystyle.css">
</head>
<body>
  <div class="container-fluid" align="center">
    <!-- nav -->
    <nav class="navbar navbar-light bg-light">
      <a href="<c:url value="/"/>"><img alt="logo" src="public/images/spring_50x50.png"></a>
      <a href="form" class="btn btn-outline-success my-2 my-sm-0">Save new employee</a>
    </nav>
    <!-- header -->
    <div class="container">
      <div class="row">
        <div class="col-lg-12" align="center">
          <img alt="logo" src="public/images/logo.png">
        </div>
      </div>
    </div>
    <div class="main">
      <div class="col-lg-9" style="margin-top: 50px;">
        <h2 class="bg-primary text-white">List of employees</h2>
        <table class="table">
          <thead class="thead-dark">
            <tr align="center">
              <th scope="col">ID</th>
              <th scope="col">NAME</th>
              <th scope="col">E-MAIL</th>
              <th scope="col">EDIT</th>
              <th scope="col">DELETE</th>
            </tr>
          </thead>
          <tbody>
            <c:forEach items="${employees}" var="employee">
              <tr align="center">
                <td>${employee.id}</td>
                <td>${employee.firstName} ${employee.lastName}</td>
                <td>${employee.email}</td>
                <td><a href="update?id=${employee.id}"><i class="fa fa-edit"></i></a></td>
                <td><a href="delete?id=${employee.id}"><i class="fa fa-times"></i></a></td>
              </tr>
            </c:forEach>
          </tbody>
        </table>
      </div>
    </div>
  </div>
  <!-- jQuery core JavaScript -->
    <script src="public/js/jquery.min.js"></script>
    <!-- Bootstrap core JavaScript -->
    <script src="public/js/bootstrap.min.js"></script>
</body>
</html>
                    

El index presenta una lista de los empleados con los botones correspondientes para llevar a cabo las operaciones básicas (guardar, borrar, editar).

run app
employee-form.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>employee form</title>
<link rel="stylesheet" href="public/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css">
<link rel="stylesheet" href="public/css/mystyle.css">
</head>
<body>
  <div class="container-fluid" align="center">
    <!-- nav -->
    <nav class="navbar navbar-light bg-light">
      <a href="<c:url value="/"/>"><img alt="logo" src="public/images/spring_50x50.png"></a>
      <a href="form" class="btn btn-outline-success my-2 my-sm-0">Save new employee</a>
    </nav>
    <div class="col-lg-6" style="margin-bottom: 50px">
      <h2 class="bg-primary text-white">Employee Manager</h2>
    </div>
    <div class="col-lg-6" style="margin-top: 50px;">
      <div class="card" align="left">
        <h5 class="card-title p-3 mb-2 text-light bg-dark">New Employee</h5>
        <div class="card-body">
          <form:form action="save" method="post" modelAttribute="employee" cssClass="form-horizontal">
            <!-- first name -->
              <div class="form-group">
                <label for="firstName" class="col-md-3 control-label font-weight-bold">First Name</label>
                <div class="col-md-9">
                  <form:input path="firstName" type="text" cssClass="form-control" />
                  <form:errors path="firstName" cssClass="errors" />
                </div>
              </div>
              <!-- last name -->
              <div class="form-group">
                <label for="lastName" class="col-md-3 control-label font-weight-bold">Last Name</label>
                <div class="col-md-9">
                  <form:input path="lastName" type="text" cssClass="form-control" />
                  <form:errors path="lastName" cssClass="errors" />
                </div>
              </div>
              <!-- email -->
              <div class="form-group">
                <label for="email" class="col-md-3 control-label font-weight-bold">Email</label>
                <div class="col-md-9">
                  <form:input path="email" type="email" cssClass="form-control" />
                  <form:errors path="email" cssClass="errors" />
                </div>
              </div>
              <div class="form-group">
              <!-- Button -->
              <div class="col-md-offset-3 col-md-9">
                <form:button type="Submit" class="btn btn-primary">Save</form:button>
              </div>
            </div>
          </form:form>
        </div>
      </div>
    </div>
  </div>
  <!-- jQuery core JavaScript -->
    <script src="public/js/jquery.min.js"></script>
    <!-- Bootstrap core JavaScript -->
    <script src="public/js/bootstrap.min.js"></script>
</body>
</html>
                    

Esta vista es un simple formulario para introducir en la base de datos un nuevo empleado.

run app
employee-update-form.jsp

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>employee update form</title>
<link rel="stylesheet" href="public/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css">
<link rel="stylesheet" href="public/css/mystyle.css">
</head>
<body>
  <div class="container-fluid" align="center">
    <!-- nav -->
    <nav class="navbar navbar-light bg-light">
      <a href="<c:url value="/"/>"><img alt="logo" src="public/images/spring_50x50.png"></a>
      <a href="form" class="btn btn-outline-success my-2 my-sm-0">Save new employee</a>
    </nav>
    <div class="col-lg-10" style="margin-bottom: 100px">
      <h1>Employee Manager</h1>
    </div>
    <div class="col-lg-6" style="margin-top: 50px;">
      <div class="card" align="left">
        <h5 class="card-title p-3 mb-2 text-light bg-dark">Update employee</h5>
        <div class="card-body">
          <form:form action="save" method="post" modelAttribute="employee" cssClass="form-horizontal">
            <!-- ID -->
              <div class="form-group">
                <label for="id" class="sr-only">ID</label>
                <div class="col-md-9">
                  <form:input path="id" type="text" cssClass="form-control" readonly="true"/>
                </div>
              </div>
            <!-- first name -->
              <div class="form-group">
                <label for="firstName" class="col-md-3 control-label font-weight-bold">First Name</label>
                <div class="col-md-9">
                  <form:input path="firstName" type="text" cssClass="form-control" />
                  <form:errors path="firstName" cssClass="errors" />
                </div>
              </div>
              <!-- last name -->
              <div class="form-group">
                <label for="lastName" class="col-md-3 control-label font-weight-bold">Last Name</label>
                <div class="col-md-9">
                  <form:input path="lastName" type="text" cssClass="form-control" />
                  <form:errors path="lastName" cssClass="errors" />
                </div>
              </div>
              <!-- email -->
              <div class="form-group">
                <label for="email" class="col-md-3 control-label font-weight-bold">Email</label>
                <div class="col-md-9">
                  <form:input path="email" type="email" cssClass="form-control" />
                  <form:errors path="email" cssClass="errors" />
                </div>
              </div>
              <div class="form-group">
              <!-- Button -->
              <div class="col-md-offset-3 col-md-9">
                <form:button type="Submit" class="btn btn-primary">Save</form:button>
              </div>
            </div>
          </form:form>
        </div>
      </div>
    </div>
  </div>
    <!-- jQuery core JavaScript -->
    <script src="public/js/jquery.min.js"></script>
    <!-- Bootstrap core JavaScript -->
    <script src="public/js/bootstrap.min.js"></script>
</body>
</html>
                    

Y la última es un formulario para poder editar a los empleados cuando sea necesario.

run app

Herramientas y Software

  • Eclipse Version: 2019-03 (4.11.0)
  • jdk 1.8
  • Maven 3.6
  • Spring MVC 5.1.5
  • Hibernate 5.4.1. Final
  • MySQL 8.0.15
  • bootstrap 4.3.1
  • Apache Tomcat v8.5
anterior

Internacionalización i18n

siguiente

Spring Data JPA