JaVers needs funding to sustain. You can sponsor JaVers development easily via GitHub Sponsors or Open Collective.

Spring integration

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:

The javers-spring-jpa module (a superset of the javers-spring module) which provides:

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:6.2.2'

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:6.2.2'

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:

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:

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

Here is a working example of Spring Application Context with all JaVers beans, JPA, Hibernate, Spring Data and Spring TransactionManager.

package org.javers.spring.example;

import ...

@Configuration
@ComponentScan(basePackages = "org.javers.spring.repository")
@EnableTransactionManagement
@EnableAspectJAutoProxy
@EnableJpaRepositories({"org.javers.spring.repository"})
public class JaversSpringJpaApplicationConfig {

    //.. 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 TransactionalJaversBuilder
                .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 javersAuditable(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 javersSpringDataAspect(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 ..
}

Spring configuration example for MongoDB

Here is a working example of Spring Application Context with a JaVers instance, JaVers auto-audit aspects, and Spring Data MongoDB.

package org.javers.spring.example;

import ...

@Configuration
@ComponentScan(basePackages = "org.javers.spring.repository")
@EnableMongoRepositories({"org.javers.spring.repository"})
@EnableAspectJAutoProxy
public class JaversSpringMongoApplicationConfig {
    private static final String DATABASE_NAME = "mydatabase";

    /**
     * Creates JaVers instance backed by {@link MongoRepository}
     */
    @Bean
    public Javers javers() {
        MongoRepository javersMongoRepository =
                new MongoRepository(mongo().getDatabase(DATABASE_NAME));

        return JaversBuilder.javers()
                .registerJaversRepository(javersMongoRepository)
                .build();
    }

    /**
     * MongoDB setup
     */
    @Bean(name="realMongoClient")
    @ConditionalOnMissingBean
    public MongoClient mongo() {
        return new MongoClient();
    }

    /**
     * required by Spring Data MongoDB
     */
    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), DATABASE_NAME);
    }

    /**
     * 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() {
        return new JaversAuditableAspect(javers(), authorProvider(), commitPropertiesProvider());
    }

    /**
     * Enables auto-audit aspect for Spring Data repositories. <br/>
     *
     * Use {@link org.javers.spring.annotation.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) {
                if (domainObject instanceof DummyObject) {
                    return Maps.of("dummyObject.name", ((DummyObject)domainObject).getName());
                }
                return Collections.emptyMap();
            }
        };
    }
}