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 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.sqland data.sqlfrom 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 .AbstractAuditableUPKidcreatedBycreatedDatelastModifiedBylastModifiedDate
nameversionname

  • @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 file orm.xmlwith 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 nameproperty 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.CrudRepositoryT 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.

Also popular now: