
Spring Data JPA Audit and Version Magic Example
An example of the magic of Spring Boot , Spring Data JPA and entity auditing.
Although the entire configuration will be described in classes using Java Config, there is a file in the application
We will use the H2 Database Engine as the database .
By default, the Spring Boot for Spring Data JPA when connecting the HSQL, H2, or Derby database driver creates a DataSource with an in-memory database and initializes it with files
addition to the DataSource, Spring Boot will kindly create an EntityManagerFactory, which will find entities anywhere in the application.
To further customize the application, create the AppConfig class:
This is what the class does
Now you need to create entity classes, start with User:
We will inherit from an abstract class from Spring Data, where is the type that is responsible for the user, and is the type of the main key. As a result of this inheritance in effect already be following properties: , , , and . For convenience, add a property , and for the version number, the property that Spring Data will manage. Actually, Spring Data will manage all fields except .
The correspondence of the properties and fields of the table, as well as the name of the table, Spring will establish itself, of course, when they match, the only presence or absence of a separator is allowed in the form of underlining. I won’t give the
second class
Now create a repository for each entity:
Actually, this is all the creation of the repository, thanks to the abstract class , where is the type of entity, is the type of the main key. The rest of the repository implementation is taken over by Spring Data. Now we create a service that is needed only to demonstrate the audit work with two different users and is created for order and beauty.
And now, in fact, the application class:
Spring magic works.
Application sources: Spring Data JPA Audit and Version Example .
PS
Nuances:
* Joda-Time dependency was added to the project , without it the magic with timestamp does not work and you will have to manually specify the createdDate and lastModifiedDate fields and their type.
* The user is added with the following fields:
If the version is NULL, an error will occur in the wilds of Spring Data, if USER is specified as the creator or modifier of the same source, an error will occur due to an endless circular reference inside Spring Data.
Although the entire configuration will be described in classes using Java Config, there is a file in the application
application.properties
. It is used because Spring Boot picks up these settings at the very early stage of initialization, and some default settings should be replaced. We will use the H2 Database Engine as the database .
By default, the Spring Boot for Spring Data JPA when connecting the HSQL, H2, or Derby database driver creates a DataSource with an in-memory database and initializes it with files
schema.sql
and data.sql
from application resources. Also used by defaulthibernate.hbm2ddl.auto=create-drop
, after which we get a pristine database with tables generated from entities. Why this is done is a mystery, but this auto-generation must be disabled by the parameter in the file application.properties
: In spring.jpa.hibernate.ddl-auto=none
addition to the DataSource, Spring Boot will kindly create an EntityManagerFactory, which will find entities anywhere in the application.
To further customize the application, create the AppConfig class:
@Configuration
@EnableTransactionManagement
@EnableJpaAuditing
public class AppConfig
{
@Bean
public AuditorAware auditorProvider() {
return new AuditorAwareImpl();
}
}
@Configuration
- informs Spring Boot that this file contains beans to configure the application;@EnableTransactionManagement
- Includes transaction support and creates the necessary beans with default settings;@EnableJpaAuditing
- includes audit support, but the bin for the work of this support will still have to be written by ourselves to explain spring where to get the user from;
This is what the class does
AuditorAwareImpl
:public class AuditorAwareImpl implements AuditorAware {
@Autowired
private CurrentUserService currentUserService;
@Override
public User getCurrentAuditor() {
return currentUserService.getCurrentUser();
}
}
CurrentUserService
- this is a service that will give the User object, we will create it a bit later. Now you need to create entity classes, start with User:
@Entity
@EntityListeners({AuditingEntityListener.class})
public class User extends AbstractAuditable {
@Basic
@Column
private String name;
public String getName() {
return name;
}
public void setName(String data) {
this.name = data;
}
@Version
@Column
private Long version;
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
@Override
public String toString() {
return "User {" +
"id='" + getId() + "', " +
"name='" + getName() + "'} ";
}
}
We will inherit from an abstract class from Spring Data, where is the type that is responsible for the user, and is the type of the main key. As a result of this inheritance in effect already be following properties: , , , and . For convenience, add a property , and for the version number, the property that Spring Data will manage. Actually, Spring Data will manage all fields except .
AbstractAuditable
U
PK
id
createdBy
createdDate
lastModifiedBy
lastModifiedDate
name
version
name
@Entity
- inform JPA that it is an entity class@EntityListeners({AuditingEntityListener.class})
- add the default listener class from Spring Data for the entity. This line eliminates the need for a fileorm.xml
with the same setting.
The correspondence of the properties and fields of the table, as well as the name of the table, Spring will establish itself, of course, when they match, the only presence or absence of a separator is allowed in the form of underlining. I won’t give the
second class
Foo
, it has a name
property instead data
. Now create a repository for each entity:
public interface UserRepository extends CrudRepository { }
Actually, this is all the creation of the repository, thanks to the abstract class , where is the type of entity, is the type of the main key. The rest of the repository implementation is taken over by Spring Data. Now we create a service that is needed only to demonstrate the audit work with two different users and is created for order and beauty.
CrudRepository
T
ID
CurrentUserService
@Service
public class CurrentUserService {
private Long currentUserID = 1L;
@Autowired
private UserRepository userRepository;
public User getCurrentUser() {
return userRepository.findOne(currentUserID);
}
public void setCurrentUserToJohn() {
currentUserID = 1L;
}
public void setCurrentUserToDoe() {
currentUserID = 2L;
}
}
@Service
- tells Spring that this class implements an internal service.@Autowired
- using dependency injection creates an instance of the user repository.
And now, in fact, the application class:
@ComponentScan
@EnableAutoConfiguration
public class App implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Autowired
private FooRepository fooRepository;
@Autowired
private CurrentUserService currentUserService;
@Override
public void run(String... args) {
Foo o = new Foo();
o.setData("test data");
fooRepository.save(o);
fooRepository.findAll().forEach(System.out::println);
currentUserService.setCurrentUserToDoe();
o.setData("New test data");
fooRepository.save(o);
fooRepository.findAll().forEach(System.out::println);
}
}
Spring magic works.
Application sources: Spring Data JPA Audit and Version Example .
PS
Nuances:
* Joda-Time dependency was added to the project , without it the magic with timestamp does not work and you will have to manually specify the createdDate and lastModifiedDate fields and their type.
* The user is added with the following fields:
insert into USER (ID, NAME, VERSION) values (1, 'John', 0);
If the version is NULL, an error will occur in the wilds of Spring Data, if USER is specified as the creator or modifier of the same source, an error will occur due to an endless circular reference inside Spring Data.