Persistencia en BD con Spring Data JPA (III): Auditoría

Tercera parte del artículo dedicado a la persistencia en BD con Spring Data JPA:

  1. Primeros pasos
  2. Repositorios personalizados
  3. Auditoría

Spring Data JPA proporciona un mecanismo sencillo y elegante para dotar de un sistema automatico de auditoría
a las entidades. Los campos de auditoría contemplados son la fecha de creación, fecha de última modificación, el usuario creador y el último que modificó la entidad. Vamos a implementar este mecanismo en nuestra demo paso a paso.

  1. Comprobar que tenemos la dependencia spring-aspects. En función de los módulos de Spring que estemos usando es muy posible que ya la tengamos, si no la añadimos:

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>{spring.version}</version>
    <dependency>
    

    En cualquier caso, si no tenemos esta librería en el classpath la excepción que se produce es muy descriptiva:

    Configuration problem: Could not configure Spring Data JPA auditing-feature because spring-aspects.jar is not on the classpath!
  2. Añadir los campos de auditoría a las entidades. Generalmente crearemos una superclase con estos campos que será especializada por todas las entidades que requieran auditoría. Los atributos correspondientes a la auditoría se anotarán con @CreatedBy, @LastModifiedBy, @CreatedDate y @LastModifiedDate. Para las fechas se puede utilizar Date, Calendar o los DateTime de Joda-Time, y para el usuario cualquier clase. En el ejemplo se utilizará un String por simplicidad pero en una aplicación real lo habitual será utilizar una clase User o similar del modelo de datos.
    package com.danielme.demo.springdatajpa.model;
    
    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;
    
    	public Calendar getCreatedDate()
    	{
    		return createdDate;
    	}
    
    	public void setCreatedDate(Calendar createdDate)
    	{
    		this.createdDate = createdDate;
    	}
    
    	public Calendar getLastModifiedDate()
    	{
    		return lastModifiedDate;
    	}
    
    	public void setLastModifiedDate(Calendar lastModifiedDate)
    	{
    		this.lastModifiedDate = lastModifiedDate;
    	}	
    
    	public String getCreateBy()
    	{
    		return createBy;
    	}
    
    	public void setCreateBy(String createBy)
    	{
    		this.createBy = createBy;
    	}
    
    	public String getLastModifiedBy()
    	{
    		return lastModifiedBy;
    	}
    
    	public void setLastModifiedBy(String lastModifiedBy)
    	{
    		this.lastModifiedBy = lastModifiedBy;
    	}      
        
        
    }
    
    

    Esta clase además está anotada con @MappedSuperclass para que las entidades hijas incluyan sus atributos en la tabla correspondiente y con @EntityListeners para que se ejecute el listener de auditoría de Spring Data. Si hacemos que Country herede de AuditableEntity el modelo de datos quedará tal que así:

    class diagram

    Una alternativa consiste en hacer que la entidadaes auditables hereden de la clase AbstractAuditable de Spring Data JPA, equivalente a la clase AuditableEntity que hemos creado, o incluso hacer que implementen directamente la interfaz Auditable. En ambos casos perdemos flexibilidad y no obtenemos ninguna ventaja aparente salvo que puntualmente haya que utilizar la interfaz al no disponer Java de herencia múltiple.

    spring data jpa auditable

  3. Implementar la interfaz AuditorAware. Esta implementación será un bean de Spring por lo que se anotará con @Component y tendrá un método que devolverá un objeto correspondiente al usuario que se utilizará en los campos de auditoría (en una misma transacción este método sólo se invocará la primera vez que sea necesario obtener el usuario). En el ejemplo el nombre de usuario se tomará de un atributo estático, en una aplicación real se obtendrá del sistema de gestión de usuarios que tengamos implementado, por ejemplo Spring Security.
    package com.danielme.demo.springdatajpa.listeners;
    
    import org.springframework.data.domain.AuditorAware;
    import org.springframework.stereotype.Component;
    
    import com.danielme.demo.springdatajpa.AuthenticationMockup;
    
    @Component
    public class CustomAuditorAware implements AuditorAware<String>;
    {
    
    	@Override
    	public String getCurrentAuditor()
    	{
    		return AuthenticationMockup.UserName;
    	}
    
    }
    
    
  4. Definir nuestro AuditorAware en al applicationContext.xml de la siguiente forma:
    <jpa:auditing auditor-aware-ref="customAuditorAware" />;	
    
    1. Siguiendo estos pasos ya tenemos la auditoría configurada. Se puede probar con el siguiente test:

      	@Test
      	public void testAudit() throws Exception
      	{
      		Country country = new Country();
      		country.setName("Bolivia");
      		country.setPopulation(10556105);
      
      		country = countryRepository.save(country);
      		assertTrue(country.getCreateBy().equals(country.getLastModifiedBy()));
      		assertTrue(country.getCreatedDate().equals(country.getLastModifiedDate()));
      
      		Thread.sleep(2000);
      
      		AuthenticationMockup.UserName = "update";
      		country.setName("Estado Plurinacional de Bolivia");
      		country = countryRepository.save(country);
      		assertTrue(country.getLastModifiedBy().equals(AuthenticationMockup.UserName));
      		assertTrue(country.getCreateBy().equals("dani"));
      		assertFalse(country.getCreatedDate().equals(country.getLastModifiedDate()));
      
      		AuthenticationMockup.UserName = "dani";
      		countryRepository.delete(country);
      
      	}
      ...
      

      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.

One Response to Persistencia en BD con Spring Data JPA (III): Auditoría

  1. Sergio dice:

    Muy buen articulo.
    Si se usa la implementacion de hibernate y se quiere una auditoria de los datos cambiados de una BBDD, se puede usar Hibernate Envers.
    Os dejo un buen enlace que encontré hace tiempo
    https://jrodriguezweb.wordpress.com/2013/10/29/hibernate-envers-ejemplo/

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 )

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 )

Google+ photo

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

Conectando a %s

A %d blogueros les gusta esto: