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> {
}