📜 ⬆️ ⬇️

Sample Magic Spring Data JPA Audit and Version

An example of the work of magic Spring Boot , Spring Data JPA and entity auditing.

Although the entire configuration will be described in classes using Java Config, the application has an application.properties file. It is used because Spring Boot picks up these settings at the earliest initialization stage, and some default settings should be replaced.

We will use the H2 Database Engine as the database.
')
By default, Spring Boot for Spring Data JPA, when connecting an HSQL, H2 or Derby database driver, creates a DataSource with an in-memory database and initializes it with the schema.sql and data.sql from application resources. Also, the default is hibernate.hbm2ddl.auto=create-drop , after which we get a pristine-free database with tables generated from entities. Why this is done is a mystery, but this auto spring.jpa.hibernate.ddl-auto=none should be turned off by the parameter in the application.properties file: spring.jpa.hibernate.ddl-auto=none

In addition to the DataSource, Spring Boot will also kindly create an EntityManagerFactory that will find entities anywhere in the application.

To further configure the application, create an AppConfig class:

 @Configuration @EnableTransactionManagement @EnableJpaAuditing public class AppConfig { @Bean public AuditorAware<User> auditorProvider() { return new AuditorAwareImpl(); } } 


This class is engaged in the AuditorAwareImpl :

 public class AuditorAwareImpl implements AuditorAware<User> { @Autowired private CurrentUserService currentUserService; @Override public User getCurrentAuditor() { return currentUserService.getCurrentUser(); } } 

CurrentUserService is a service that will return a User object, create it a little later.

Now you need to create entity classes, let's start with User:

 @Entity @EntityListeners({AuditingEntityListener.class}) public class User extends AbstractAuditable<User, Long> { @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() + "'} "; } } 

Inheriting from the AbstractAuditable<U, PK> abstract class from Spring Data, where U is the type that represents the user, and PK is the type of the primary key. As a result of this inheritance, the following properties will already be in essence: id , createdBy , createdDate , lastModifiedBy and lastModifiedDate .
For convenience, we add the name property, and for the version number, the version property that Spring Data will manage. Actually Spring Data will manage all fields except name .



Matching the properties and fields of the table, as well as the name of the table, Spring will establish itself, of course, if they match, the only thing that is allowed is the presence or absence of a separator in the form of underscores.

I will not give the second class of Foo , instead of its name data property.

Now we create a repository for each entity:
 public interface UserRepository extends CrudRepository<User, Long> { } 

Actually, this is all the creation of a repository, thanks to the abstract class CrudRepository<T, ID> , where T is the type of the entity, ID is the type of the main key. The rest of the implementation of the repository takes Spring Data.

Now we will create the CurrentUserService service, which is needed only to demonstrate the work of the audit with two different users and is designed for order and beauty.

 @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; } } 



And now, actually, 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 is added to the project, without it, the magic with the timestamp does not work and you will have to manually specify the createdDate and lastModifiedDate fields and their type.
* User is added with such 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 you specify USER as the creator or modifier of it yourself, an error will occur due to the infinite circular reference inside Spring Data.

Source: https://habr.com/ru/post/238487/


All Articles