Spring Data JPA Auditing Example – SpringBoot RestAPIs + MySQL

spring-jpa-auditing-spring-data-mysql-feature-image

In the tutorial, we show how to build a SpringBoot application with a Database Auditing feature for tracking the change (with 2 fields created_at & updated_at) by using Spring Data JPA, Java 8 DateTime, and MySQL.

Related posts:
How to use Spring JPA MySQL | Spring Boot
Spring JPA Hibernate One to Many Relationship – SpringBoot + MySQL

Technologies

– Java 8
– SpringBoot 2.0.6.RELEASE
– MySQL

Goal

We create a SpringBoot project as below structure:

spring-jpa-auditing-spring-data-mysql-project-structure

Run & Check results:

-> Post request:

spring-jpa-auditing-spring-data-mysql-post-customer

spring-jpa-auditing-spring-data-mysql-after-posts-database

-> Put request:

spring-jpa-auditing-spring-data-mysql-put-customer

spring-jpa-auditing-spring-data-mysql-put-customer-check-record

With 2 fields created_at & updated_at, we can track the change of database’s records.

How to achieve it? -> We use Spring JPA Auditing.
– Firstly, configure JPA Auditing:

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}

– Then we create a DateAudit class as below:

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
        value = {"createdAt", "updatedAt"},
        allowGetters = true
)
public abstract class DateAudit {

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private Instant updatedAt;
	
	// getters & setters
	// ...

– Domain class Customer need inherit from DateAudit class:

@Entity
@Table(name = "customers")
public class Customer extends DateAudit {

// ...

Pratice

Create SpringBoot project

– We create a SpringBoot project with below dependencies:


	org.springframework.boot
	spring-boot-starter-data-jpa


	org.springframework.boot
	spring-boot-starter-web


    mysql
    mysql-connector-java



    com.fasterxml.jackson.datatype
    jackson-datatype-jsr310

jackson-datatype-jsr310 is used to support Java 8 Date/Time.

JPA Audit Implementation

– Configure JPA Auditing:

package com.grokonez.jpaaudit.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {}

– Implement DateAudit ->

package com.grokonez.jpaaudit.model.audit;

import java.time.Instant;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
        value = {"createdAt", "updatedAt"},
        allowGetters = true
)
public abstract class DateAudit {

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private Instant updatedAt;

    public Instant getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Instant createdAt) {
        this.createdAt = createdAt;
    }

    public Instant getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Instant updatedAt) {
        this.updatedAt = updatedAt;
    }
}

@EntityListeners annotation is used to specify callback listener classes. We use the JPA entity listener class of Spring Data AuditingEntityListener.
@MappedSuperClass annotation is used to move the properties to a base class DateAudit which would be extended by all your audited entities.

Create Model class

– Create JsonView Customer class:

package com.grokonez.jpaaudit.model.view;

public class View {
	public static interface Customer {}
}

– Create Customer model class:

package com.grokonez.jpaaudit.model;

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 com.fasterxml.jackson.annotation.JsonView;
import com.grokonez.jpaaudit.model.audit.DateAudit;
import com.grokonez.jpaaudit.model.view.View;
 
@Entity
@Table(name = "customers")
public class Customer extends DateAudit {
	@JsonView(View.Customer.class)
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@JsonView(View.Customer.class)
	@Column(name = "name")
	private String name;
	
	@JsonView(View.Customer.class)
	@Column(name = "address")
	private String address;

	@JsonView(View.Customer.class)
	@Column(name = "age")
	private int age;
 
	public Customer() {
	}
 
	public Customer(Long id, String name, String address, int age) {
		this.id = id;
		this.name = name;
		this.address = address;
		this.age = age;
	}
 
	public Long getId() {
		return id;
	}
 
	public void setId(Long id) {
		this.id = id;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	public String getAddress() {
		return address;
	}
 
	public void setAddress(String address) {
		this.address = address;
	}
 
	public int getAge() {
		return age;
	}
 
	public void setAge(int age) {
		this.age = age;
	}
 
	@Override
	public String toString() {
		return "Customer [id=" + id + ", name=" + name + ", address=" + address + ", age=" + age + "]";
	}
 
}

@JsonView annotation of Jackson library is used to serialize/de-serialize Java objects and customize the fields of JSON view.

-> See more at: How to use @JsonView to serialize/de-serialize and customize JSON format from Java Object

Create JPA Repository

CustomerRepository.java ->

package com.grokonez.jpaaudit.repository;

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

import com.grokonez.jpaaudit.model.Customer;

public interface CustomerRepository extends JpaRepository{
}
RestAPIs Controller Implementation

CustomerRestAPIs.java ->

package com.grokonez.jpaaudit.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.annotation.JsonView;
import com.grokonez.jpaaudit.model.Customer;
import com.grokonez.jpaaudit.model.view.View;
import com.grokonez.jpaaudit.repository.CustomerRepository;

@RestController
@RequestMapping("/api/customers")
public class CustomerRestAPIs {
	
	@Autowired
	CustomerRepository repository;
	
    @PostMapping
    public Customer saveCustomer(@Valid @RequestBody Customer customer) {
    	return repository.save(customer);
    }

    @PutMapping
    public Customer updatedCustomer(@Valid @RequestBody Customer updatedCustomer) {
        return repository.findById(updatedCustomer.getId())
                .map(customer -> {
                    customer.setName(updatedCustomer.getName());
                    customer.setAddress(updatedCustomer.getAddress());
                    customer.setAge(updatedCustomer.getAge());
                    return repository.save(customer);
                }).orElseThrow(() -> new RuntimeException ("Customer not found with id " + updatedCustomer.getId()));
    }
    
    @GetMapping
    @JsonView(View.Customer.class)
    public List getAll(){
    	return repository.findAll();
    }
}
Main Class Implementation

– Set the default timezone for our application to UTC.

package com.grokonez.jpaaudit;

import java.util.TimeZone;

import javax.annotation.PostConstruct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;

@SpringBootApplication
@EntityScan(basePackageClasses = { 
		SpringBootJpaAuditApplication.class,
		Jsr310JpaConverters.class 
})
public class SpringBootJpaAuditApplication {
	
	@PostConstruct
	void init() {
		TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaAuditApplication.class, args);
	}
}
Application Configuration

application.properties ->

spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=root
spring.datasource.password=12345
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop

## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG

## Jackson Properties
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS= false
spring.jackson.time-zone= UTC
Run & Check results

Start SpringBoot project,

-> MySQL table:

spring-jpa-auditing-spring-data-mysql-start-project-table-schema

– Post customers ->

spring-jpa-auditing-spring-data-mysql-post-customer

spring-jpa-auditing-spring-data-mysql-after-posts-database

– Update customer ->

spring-jpa-auditing-spring-data-mysql-put-customer

spring-jpa-auditing-spring-data-mysql-put-customer-check-record

– Get customers ->

spring-jpa-auditing-spring-data-mysql-get-all-customer

SourceCode

SpringBootJpaAudit



By grokonez | October 30, 2018.

Last updated on February 6, 2020.



Related Posts


2 thoughts on “Spring Data JPA Auditing Example – SpringBoot RestAPIs + MySQL”

Got Something To Say:

Your email address will not be published. Required fields are marked *

*