Extend findAll with custom filters

2

I'm trying to extend findAll from my JPA repository to make custom filters.

I did some research and found some ideas of use with Predicates and related but did not identify something that worked in a practical way.

I would like to know if anyone has ever had something similar or have any idea how to do this without having to create several forms of search within my Repository.

    
asked by anonymous 26.05.2015 / 16:16

2 answers

1

One of the ways to modify the default behavior of a repository method with Spring Data JPA is by creating another base implementation for repository. The documentation shows how adds behavior custom to the repository.

The steps to follow are more or less these:

  • create a custom interface for your repositories, you can extend the JpaRepository even, something like this:
  • @NoRepositoryBean
    public interface CustomGenericRepository<E extends CustomAbstractEntity, PK extends Serializable> extends
            JpaRepository<E, PK>, JpaSpecificationExecutor<E> { }
    

    We will also extend JpaSpecificationExecutor , because our custom repository will use specifications and it supports this.

  • The second step is to implement this interface that we just defined. Here is an example that changes the behavior of findAll , using a Specification which will consider in the query only the records not deleted logically, something like this:
  • public class CustomGenericRepositoryImpl<E extends CustomAbstractEntity, PK extends Serializable> extends
            SimpleJpaRepository<E, PK> implements CustomGenericRepository<E, PK> {
    
        private final EntityManager entityManager;
    
        public CustomGenericRepositoryImpl(final Class<T> domainClass, final EntityManager entityManager) {
            super(domainClass, entityManager);
            this.entityManager = entityManager;
        }
    
        @Override
        public List<E> findAll() {
            return super.findAll(this.isRemoved());
        }
    
        private Specification<E> isRemoved() {
            return new Specification<E>() {
    
                @Override
                public Predicate toPredicate(final Root<E> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) {
                    return cb.isFalse(root.<Boolean> get("deleted"));
                }
    
            };
    
        }
    
    }
    
  • Since we have a different implementation that we will use in our repositories, we need to create a way to instruct the container spring how it will create the instances for the repositories. Then we will create a custom factory bean that also extends from one that is JPA , the JpaRepositoryFactoryBean :
  • public class CustomGenericRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends
            JpaRepositoryFactoryBean<T, S, ID> {
    
        @Override
        protected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {
            return new RepositoryFactory(entityManager);
        }
    
        private static class RepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
    
            public RepositoryFactory(final EntityManager entityManager) {
                super(entityManager);
            }
    
            protected Object getTargetRepository(final RepositoryMetadata metadata) {
                return new CustomGenericRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), em);
            }
    
            @Override
            protected Class<?> getRepositoryBaseClass(final RepositoryMetadata metadata) {
                return CustomGenericRepositoryImpl.class;
            }
    
        }
    
    }
    
  • Finally, we need to tell the spring data which is the factory-class that will create our repositories. Using XML you can use this way:
  • <jpa:repositories base-package="com.brunocesar.custom.repository,com.brunocesar.custom.repository.support"
        factory-class="com.brunocesar.custom.repository.support.factory.CustomGenericRepositoryFactoryBean"
        transaction-manager-ref="transactionManagerCustom" entity-manager-factory-ref="entityManagerFactoryCustom" />
    

    Using Java, considering a class just for configuring repositories, just set the factoryClass attribute to @EnableJpaRepositories , something like this:

    @EnableJpaRepositories(factoryClass = "com.brunocesar.custom.repository.support.factory.CustomGenericRepositoryFactoryBean")
    public class JPARepositoryConfig {
    
        // o resto da configuração continua a mesma =)
    
    }
    

    To use, you just need to re-extend the custom interface we created, CustomGenericRepository , something like this:

    @Repository
    public interface PessoaRepository extends CustomGenericRepository<Pessoa, Long> {
    
    }
    
        
    26.05.2015 / 18:58
    0

    Implements the JPARepository that you can customize your queries according to the attributes of your sample object below:

    public interface Empregado extends JpaRepository<Empregado , Integer> {
       //Atributo Endereco dentro de Empregado ele já implementa pra vc tudo!
       List<Empregado> findByEndereco(Endereco endereco );
    }
    
        
    26.06.2015 / 17:32