2

I'm beginning to work with Spring Data JPA repositories. We have an application already using Spring MVC (No spring boot or spring data JPA), where we have written a Generic DAO class that handles basic CRUD operations for virtually all entities that we have. Any other special operations can be handled by writing custom DAOs.

Now, Spring data JPA has made things very easy by requiring us to write only an interface and the rest is taken care of.

public interface PersonRepository extends JpaRepository<Person, Long> {

}

This is cool, but I was wondering if I can introduce generics here.

The reason is, my application has a number of entities for which we need to perform only basic CRUD operations and nothing more. Which means, for every entity, we need to write an interface. Though the code is minimal, it results in one file for each entity, which I guess can be avoided (true?).

My question is, can I write a generic Repository class like

public interface GenericRepository<T> extends JpaRepository<T, Long> {

}

so that my service class can look like this

@Autowired
private GenericRepository<Person> personRepository;

public List<Person> findAll() {
     return this.personRepository.findAll();
}

This will be a much cleaner approach for basic operations, as one Repository interface handles a number of entities.

EDIT It turns out that I can indeed create a repository interface as I illustrated above, but when the application starts, I get an error which says

Error creating bean with name 'genericRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class java.lang.Object

This is probably because of the Generic Type

I have to say that my entities are separate classes in themselves and do not implement or extend a super entity/entities. Would it help if they did?

Please guide me in the right direction.

Thanks!

Sriram Sridharan
  • 720
  • 18
  • 43
  • what about https://stackoverflow.com/questions/23812458/spring-data-not-an-managed-type-class-java-lang-object ? – Pijotrek Nov 08 '18 at 14:15

1 Answers1

2

I think you could do it like this:

@NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
   //added custom common functionality for all the GenericRepository implementations
   public List<T> findByAttributeContainsText(String attributeName, String text);
}

public class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
    implements GenericRepository<T, ID> {

   private EntityManager entityManager;

   public GenericRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
}

@Transactional
public List<T> findByAttributeContainsText(String attributeName, String text) {
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<T> cQuery = builder.createQuery(getDomainClass());
    Root<T> root = cQuery.from(getDomainClass());
    cQuery.select(root).where(builder.like(root.<String>get(attributeName), "%" + text + "%"));
    TypedQuery<T> query = entityManager.createQuery(cQuery);
    return query.getResultList();
}
}
public interface MyOtherRepository extends GenericRepository<Role, Long> {

}

And in your config class:

@Configuration
@EnableJpaRepositories(basePackages="com.myProject", repositoryBaseClass = 
GenericRepositoryImpl.class)
public class JpaConfig {

}
honor
  • 7,378
  • 10
  • 48
  • 76