We made JaVers easy to use in applications based on the Spring Framework. There are two modules for Spring integration:
The javers-spring
module provides the following annotations to configure
the auto-audit aspects:
-
@JaversSpringDataAuditable — choose it if your persistence layer relies on Spring Data. It’s the class-level annotation which adds the auto-audit aspect to a Spring Data
CrudRepository
. This is the easiest and recommended way to auto-audit your domain objects. -
@JaversAuditable, @JaversAuditableDelete, and @JaversAuditableConditionalDelete — it’s the family of method-level annotations to configure the auto-audit aspect for any kind of repository (non Spring Data).
The javers-spring-jpa
module (a superset of the javers-spring
module)
which provides:
- JPA & Hibernate integration for SQL databases,
- extension for @JaversSpringDataAuditable to
support Spring Data
JpaRepository
.
Dependencies
If you are using Spring Boot, simply use one of the JaVers Spring Boot starters. This is the recommended way of integrating JaVers with Spring-based applications. Our starters give you the right configuration out of the box.
Non Spring Boot applications
Take javers-spring-jpa
if you are using JaVers with
an SQL database with JPA & Hibernate:
compile 'org.javers:javers-spring-jpa:7.7.0'
Take javers-spring
if you are using JaVers with MongoDB (or any persistence stack other than SQL with JPA & Hibernate):
compile 'org.javers:javers-spring:7.7.0'
Check Maven Central for other build tools snippets.
JaVers instance as a Spring bean
We recommend integrating Spring-based applications with Javers using one of the JaVers Spring Boot starters.
If for some reason, you don’t want to use a Javers’ starter — you can configure all the Javers beans manually or copy the full Spring configuration examples for MongoDB or for JPA & Hibernate.
You need to have a JaVers instance registered as a Spring bean. For example, if you’re using MongoDB, setup JaVers as follows:
@Bean
public Javers javers() {
MongoRepository javersMongoRepository =
new MongoRepository(mongo().getDatabase("mydatabase"));
return JaversBuilder.javers()
.registerJaversRepository(javersMongoRepository)
.build();
}
@Bean
public MongoClient mongo() {
return new MongoClient();
}
Auto-audit aspects
The auto-audit aspects are based on Spring AOP. They can automatically call
proper javers.commit*(...)
methods whenever your domain objects are saved or deleted.
The aspects are configured with annotations.
There are three auto-audit aspects:
-
JaversSpringDataAuditableRepositoryAspect
for Spring DataCrudRepositories
configured with the class-level @JaversSpringDataAuditable annotation. Use it together with JaversMongoRepository
. -
JaversSpringDataJpaAuditableRepositoryAspect
for Spring DataJpaRepositories
(configured also with @JaversSpringDataAuditable). Use it together withJaversSqlRepository
. -
JaversAuditableAspect
for any kind of repositories (non Spring Data), configured with the methd-level family of annotations: @JaversAuditable, @JaversAuditableDelete, and @JaversAuditableConditionalDelete.
How the auto-audit aspects work?
After an advised method is executed,
its arguments or results are automatically committed to JaversRepository
.
If an argument is an Iterable
, JaVers iterates over it and commits each object separately.
How the auto-audit are enabled?
The auto-audit aspects will work only if you
configure them in your Spring context
or if you use one of the JaVers Spring Boot starters.
Annotations
@JaversSpringDataAuditable for Spring Data Repositories
If you’re using Spring Data, just annotate a repository you want to audit
with the class-level @JaversSpringDataAuditable
.
For example:
import org.javers.spring.data.JaversSpringDataAuditable
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
@Repository
@JaversSpringDataAuditable
interface UserCrudRepository extends CrudRepository<User, String> {
}
or
import org.javers.spring.annotation.JaversSpringDataAuditable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
@JaversSpringDataAuditable
public interface UserJpaRepository extends JpaRepository<User, String> {
}
From now, all objects passed to save()
and delete()
methods will be automatically
audited by JaVers — AWESOME!
@JaversAuditable
If you’re using ordinary Repositories (non Spring Data),
annotate all data-changing methods you want to audit with the method-level @JaversAuditable
.
For example:
@Repository
class UserRepository {
@JaversAuditable
public void save(User user) {
...//
}
public User find(String login) {
...//
}
}
In fact, you can use this method-level annotation for advising any bean in your application. It could be a Service, Repository or anything which modifies domain objects.
From now, all objects passed to the annotated methods will be automatically versioned by JaVers.
@JaversAuditableDelete
TODO
@JaversAuditableConditionalDelete
TODO
Spring configuration of auto-audit aspects
If you want to manually configure the auto-audit aspects — here is the list of beans you need:
- JaversSpringDataAuditableRepositoryAspect or JaversSpringDataJpaAuditableRepositoryAspect
- JaversAuditableAspect
- AuthorProvider
- CommitPropertiesProvider (optionally)
Note that the auto-audit aspects are based on Spring @AspectJ
.
Remember to enable @AspectJ
support by putting the @EnableAspectJAutoProxy
annotation in your Spring configuration.
For more info refer to Spring @AspectJ documentation.
JaversSpringDataAuditableRepositoryAspect bean
It defines pointcuts on save*(..)
and delete*(..)
methods
within CrudRepositories
annotated with the class-level
@JaversSpringDataAuditable annotation.
Choose it if you are not using JPA.
@Bean
public JaversSpringDataAuditableRepositoryAspect javersSpringDataAuditableAspect() {
return new JaversSpringDataAuditableRepositoryAspect(
javers(), authorProvider(), commitPropertiesProvider());
}
JaversSpringDataJpaAuditableRepositoryAspect bean
It extends the main aspect with the pointcuts on JpaRepository.saveAndFlush()
and JpaRepository.deleteInBatch()
.
It’s triggered by the same annotation — @JaversSpringDataAuditable.
Choose it if you are using JpaRepositories
.
@Bean
public JaversSpringDataJpaAuditableRepositoryAspect javersSpringDataAuditableAspect(Javers javers) {
return new JaversSpringDataJpaAuditableRepositoryAspect(
javers, authorProvider(), commitPropertiesProvider());
}
JaversAuditableAspect bean
Register it if you want to add the auto-audit feature for any kind of repository (not managed by Spring Data).
It defines the pointcuts on methods with these annotations: @JaversAuditable, @JaversAuditableDelete, or @JaversAuditableConditionalDelete.
@Bean
public JaversAuditableAspect javersAuditableAspect() {
return new JaversAuditableAspect(javers(), authorProvider(), commitPropertiesProvider());
}
AuthorProvider bean
Every JaVers commit (a data change) should be linked to its author, i.e. the user who has made the change. Please don’t confuse JaVers commit (a bunch of data changes) with SQL commit (finalizing an SQL transaction).
You need to register an implementation of the
AuthorProvider
interface,
which should return a current user name. It’s required by all auto-audit aspects.
For example:
@Bean
public AuthorProvider authorProvider() {
return new SpringSecurityAuthorProvider();
}
JaVers comes with SpringSecurityAuthorProvider
—
suitable if you are using Spring Security.
If you don’t care about commit authors, use
MockAuthorProvider
.
CommitPropertiesProvider bean
Every JaVers commit may have one or more commit properties, useful for querying (see CommitProperty filter example).
In the auto-audit aspects, commit properties are supplied by an implementation of the
CommitPropertiesProvider
interface,
for example:
@Bean
public CommitPropertiesProvider commitPropertiesProvider() {
return new CommitPropertiesProvider() {
@Override
public Map<String, String> provideForCommittedObject(Object domainObject) {
if (domainObject instanceof DummyEntity) {
return Maps.of("dummyEntityId", ((DummyEntity)domainObject).getId() + "");
}
return Collections.emptyMap();
}
};
}
If you don’t use commit properties, simply skip commitPropertiesProvider
argument
in the aspect constructor or pass new EmptyPropertiesProvider()
.
That’s the last bean in your Spring context required to run the auto-audit aspects.
JPA EntityManager integration
Transaction management is the important issue for applications backed by SQL databases.
Generally, all SQL statements executed by JaversSQLRepository
should be executed in the context of the current application’s transaction
(called Persistence Context in JPA terminology).
Read more about ConnectionProvider and JaVers’ approach to transaction management.
Spring configuration for JPA
First, you need to register exactly one transactional JaVers instance in your Spring context.
Simply use TransactionalJaversBuilder
instead of standard JaversBuilder.
Second, you need to register a transactional ConnectionProvider.
If you’re using JPA with Hibernate, choose JpaHibernateConnectionProvider
implementation
which is Persistence Context aware and plays along with Spring JpaTransactionManager.
Third, if you are using Hibernate, you need to deal with lazy-loading proxies. Hibernate silently wraps them around your Entities loaded from database. We strongly encourage to get rid of lazy-loading proxies before committing Entities to JaversRepository. It can be easily obtained with HibernateUnproxyObjectAccessHook.
See example the full Spring configuration example for JPA & Hibernate.
Hibernate unproxy hook
JaVers provides HibernateUnproxyObjectAccessHook
which is a way to unproxy
and initialize your Hibernate Entities just before processing them by JaVers diff & commit algorithms.
To use HibernateUnproxyObjectAccessHook simply bind it to your JaVers instance using JaversBuilder.withObjectAccessHook()
method:
TransactionalJaversBuilder
.javers()
.withTxManager(txManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook()).build()
Feel free to provide your own implementation of object-access
hook if you need better control over
the unproxing process.
See below for the full Spring configuration example for JPA & Hibernate.
Spring configuration example for JPA & Hibernate
Please remember, that the easiest and strongly recommended way of integrating Javers with a Spring application is using one of our Spring Boot starters.
If you are not using Spring boot — here is a working example of vanilla Spring Application Context with all JaVers beans, JPA, Hibernate, Spring Data and Spring TransactionManager.
JaversSpringJpaApplicationConfigExample.java
:
package org.javers.spring.example;
import ...
@Configuration
@ComponentScan(basePackages = "org.javers.spring.repository")
@EnableTransactionManagement
@EnableAspectJAutoProxy
@EnableJpaRepositories({"org.javers.spring.repository"})
public class JaversSpringJpaApplicationConfigExample {
//.. JaVers setup ..
/**
* Creates JaVers instance with {@link JaversSqlRepository}
*/
@Bean
public Javers javers(PlatformTransactionManager txManager) {
JaversSqlRepository sqlRepository = SqlRepositoryBuilder
.sqlRepository()
.withConnectionProvider(jpaConnectionProvider())
.withDialect(DialectName.H2)
.build();
return TransactionalJpaJaversBuilder
.javers()
.withTxManager(txManager)
.withObjectAccessHook(new HibernateUnproxyObjectAccessHook())
.registerJaversRepository(sqlRepository)
.build();
}
/**
* Enables auto-audit aspect for ordinary repositories.<br/>
*
* Use {@link org.javers.spring.annotation.JaversAuditable}
* to mark data writing methods that you want to audit.
*/
@Bean
public JaversAuditableAspect javersAuditableAspect(Javers javers) {
return new JaversAuditableAspect(javers, authorProvider(), commitPropertiesProvider());
}
/**
* Enables auto-audit aspect for Spring Data repositories. <br/>
*
* Use {@link org.javers.spring.annotation.JaversSpringDataAuditable}
* to annotate CrudRepository, PagingAndSortingRepository or JpaRepository
* you want to audit.
*/
@Bean
public JaversSpringDataJpaAuditableRepositoryAspect javersSpringDataAuditableAspect(Javers javers) {
return new JaversSpringDataJpaAuditableRepositoryAspect(javers, authorProvider(), commitPropertiesProvider());
}
/**
* Required by auto-audit aspect. <br/><br/>
*
* Creates {@link SpringSecurityAuthorProvider} instance,
* suitable when using Spring Security
*/
@Bean
public AuthorProvider authorProvider() {
return new SpringSecurityAuthorProvider();
}
/**
* Optional for auto-audit aspect. <br/>
* @see CommitPropertiesProvider
*/
@Bean
public CommitPropertiesProvider commitPropertiesProvider() {
return new CommitPropertiesProvider() {
@Override
public Map<String, String> provideForCommittedObject(Object domainObject) {
if (domainObject instanceof DummyObject) {
return Maps.of("dummyObject.name", ((DummyObject)domainObject).getName());
}
return Collections.emptyMap();
}
};
}
/**
* Integrates {@link JaversSqlRepository} with Spring {@link JpaTransactionManager}
*/
@Bean
public ConnectionProvider jpaConnectionProvider() {
return new JpaHibernateConnectionProvider();
}
//.. EOF JaVers setup ..
//.. Spring-JPA-Hibernate setup ..
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("org.javers.spring.model");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
return dataSource;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.connection.autocommit", "false");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
return properties;
}
//.. EOF Spring-JPA-Hibernate setup ..
}
MongoDB transactions support
Javers (since 6.5.1) supports multi-document ACID transactions added in MongoDB 4.0 and introduced in Spring Data MongoDB 2.1.
If you are using MongoDB multi-document transactions in your application, we recommend configuring Javers to participate in the transactions game. Thanks to that, whenever a transaction which updates your domain objects is rolled back, also corresponding Javers’ commits are rolled back.
MongoDB transactions are managed in Spring
by the well-known Spring Transaction Management platform.
The central point of this platform is the TransactionManager
abstraction
, which for MongoDB is implemented by MongoTransactionManager
.
Once you have an instance of MongoTransactionManager
you can easily build
the transactional version of Javers, which is going to participate
in your application’s transactions managed by Spring.
For example:
@Bean
public Javers javers() {
return TransactionalMongoJaversBuilder.javers()
.registerJaversRepository(new MongoRepository(mongoDatabase))
.withTxManager(mongoTransactionManager)
.build();
}
See Spring Data MongoDB Transactions by Baeldung and MongoDB 4 Update: Multi-Document ACID Transactions.
Spring configuration example for MongoDB
Please remember, that the easiest and strongly recommended way of integrating Javers with a Spring application is using one of our Spring Boot starters.
If you are not using Spring boot — here is a working example of vanilla Spring Application Context with a JaVers instance, JaVers auto-audit aspects, and Spring Data MongoDB.
JaversSpringMongoApplicationConfigExample.java
:
package org.javers.spring.mongodb.example;
import ...
@Configuration
@ComponentScan(basePackages = "org.javers.spring.repository")
@EnableMongoRepositories({"org.javers.spring.repository"})
@EnableAspectJAutoProxy
public class JaversSpringMongoApplicationConfigExample {
private static final String DATABASE_NAME = "mydatabase";
@Autowired
Optional<MongoTransactionManager> mongoTransactionManager;
/**
* Creates JaVers instance backed by {@link MongoRepository}
* <br/><br/>
*
* If you are using multi-document ACID transactions
* introduced in MongoDB 4.0 -- you can configure
* Javers' to participate in your application's transactions
* managed by MongoTransactionManager.
*/
@Bean
public Javers javers() {
return TransactionalMongoJaversBuilder.javers()
.registerJaversRepository(new MongoRepository(mongo()))
.withTxManager(mongoTransactionManager.orElse(null))
.build();
}
/**
* You can configure Javers' MongoRepository to use
* your application's primary database or a dedicated database.
*/
@Bean
public MongoDatabase mongo() {
return MongoClients.create().getDatabase(DATABASE_NAME);
}
/**
* Required by Spring Data Mongo
*/
@Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(MongoClients.create(), DATABASE_NAME);
}
/**
* Enables auto-audit aspect for ordinary repositories.<br/>
*
* Use {@link JaversAuditable}
* to mark repository methods that you want to audit.
*/
@Bean
public JaversAuditableAspect javersAuditableAspect() {
return new JaversAuditableAspect(javers(), authorProvider(), commitPropertiesProvider());
}
/**
* Enables auto-audit aspect for Spring Data repositories. <br/>
*
* Use {@link JaversSpringDataAuditable}
* to annotate CrudRepositories you want to audit.
*/
@Bean
public JaversSpringDataAuditableRepositoryAspect javersSpringDataAuditableAspect() {
return new JaversSpringDataAuditableRepositoryAspect(javers(), authorProvider(),
commitPropertiesProvider());
}
/**
* <b>INCUBATING - Javers Async API has incubating status.</b>
* <br/><br/>
*
* Enables asynchronous auto-audit aspect for ordinary repositories.<br/>
*
* Use {@link JaversAuditableAsync}
* to mark repository methods that you want to audit.
*/
@Bean
public JaversAuditableAspectAsync javersAuditableAspectAsync() {
return new JaversAuditableAspectAsync(javers(), authorProvider(), commitPropertiesProvider(), javersAsyncAuditExecutor());
}
/**
* <b>INCUBATING - Javers Async API has incubating status.</b>
* <br/><br/>
*/
@Bean
public ExecutorService javersAsyncAuditExecutor() {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("JaversAuditableAsync-%d")
.build();
return Executors.newFixedThreadPool(2, threadFactory);
}
/**
* Required by auto-audit aspect. <br/><br/>
*
* Creates {@link SpringSecurityAuthorProvider} instance,
* suitable when using Spring Security
*/
@Bean
public AuthorProvider authorProvider() {
return new SpringSecurityAuthorProvider();
}
/**
* Optional for auto-audit aspect. <br/>
* @see CommitPropertiesProvider
*/
@Bean
public CommitPropertiesProvider commitPropertiesProvider() {
return new CommitPropertiesProvider() {
@Override
public Map<String, String> provideForCommittedObject(Object domainObject) {
return Maps.of("key", "ok");
}
};
}
}