spring-data-jpa-entity-graph icon indicating copy to clipboard operation
spring-data-jpa-entity-graph copied to clipboard

spring-data-jpa-entity-graph is incompatible with spring-data-envers

Open federicotg opened this issue 1 year ago • 2 comments

What steps will reproduce the problem ?

Steps to reproduce the behavior:

  1. Set up a project using envers using @EnableEnversRepositories
  2. Set repositoryFactoryBeanClass = EntityGraphJpaRepositoryFactoryBean.class
  3. Spring context does not start: Error is Caused by: org.springframework.data.mapping. PropertyReferenceException: No property 'findRevisions' found for type 'CashCost'

What is the expected output ?

Spring context should start and the Envers repositories should work normally.

What happens instead ?

Spring context fails to start.

Environment

  • Spring Data JPA version 3.2.3
  • ORM with version: Hibernate 6.4.4
  • spring-data-jpa-entity-graph version: 3.2.2
  • spring-data-envers 3.2.3

Additional context

The problem is @EnableEnversRepositories uses its own repositoryFactoryBeanClass that is EnversRevisionRepositoryFactoryBean and EntityGraphJpaRepositoryFactoryBean does not extend that one. I can't create my own EnversRevisionRepositoryFactoryBean subclass adding the RepositoryEntityManagerEntityGraphInjector because it is package private. Envers is a popular hibernate audit log implementation and spring-data-envers is a spring-data module to deal with envers in a spring-data way.

federicotg avatar May 02 '24 17:05 federicotg

There's a workaround. Don't use @EnableEnversRepositories, just keep @EnableJpaRepositories and define manually each RevisionRepository like this

public @Bean
    RevisionRepository<MyEntity, Integer, Long> myEntityRevisions() {
        final JpaEntityInformation<MyEntity, ?> entityInformation = JpaEntityInformationSupport.getEntityInformation(MyEntity.class, entityManager);
        final RevisionEntityInformation revisionEntityInformation = new ReflectionRevisionEntityInformation(MyCustomRevision.class);
        return new EnversRevisionRepositoryImpl<>(entityInformation, revisionEntityInformation, entityManager);
    }

or like this:

public @Bean
    RevisionRepository<MyEntity, String, Integer> myEntityRevisionRepository() {
        final JpaEntityInformation<MyEntity, ?> entityInformation = JpaEntityInformationSupport.getEntityInformation(MyEntity.class, entityManager);
        final RevisionEntityInformation revisionEntityInformation = new DefaultRevisionEntityInformation();
        return new EnversRevisionRepositoryImpl<>(entityInformation, revisionEntityInformation, entityManager);
    }

As explained here:

https://stackoverflow.com/questions/65940151/dynamically-creating-spring-enversrevisionrepository-from-a-java-class

federicotg avatar May 15 '24 01:05 federicotg

Alternative, your can create two different repository and configure annotation @EnableJpaRepositories and @EnableEnversRepositories like annotation filter or use different package

@HistoryRepository
public interface MyEntityHistoryRepository extend RevisionRepository<MyEntity, Long, Long> {}

and

public interface MyEntityRepository extend JpaRepository<MyEntity, Long> {}

configure JpaRepository

@Configuration
@EnableJpaRepositories(excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = HistroyRepository.class))
public class JpaConfiguration {}

configure EnversRepository

@Configuration
@EnableEnversRepositories(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = HistroyRepository.class))
public class JpaHistoryConfiguration {}

Or you can create custom repository factory bean like this

dima-bzz avatar May 15 '24 05:05 dima-bzz