Tobi
Tobi

Reputation: 2040

Generic JPA repository for multiple entities

I have several entities and use Spring Data JPA repositories with specifications query my database. Therefore I created a generic class SpecBuilder to build my queries based on a query description (MyQueryDescriptor).

public class Specs {
  public static <T extends MyEntityIFace> Specification<T> myfind(final MyQueryDescriptor qDesc) {
    return new Specification<T>() {
      @Override
      public Predicate toPredicate(Root<T> root, 
               CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        try {
          return SpecBuilder.mySpec(root, criteriaQuery, criteriaBuilder, qDesc);
        } catch (Exception e) {
          ...handle error...
        }
      }
    };
  }
}

My repositories:

public interface Entity1DAO extends Repository<Entity1,Long>, 
                                    JpaSpecificationExecutor {
}

and

public interface Entity2DAO extends Repository<Entity2,Long>, 
                                    JpaSpecificationExecutor {
}

Now there are 3 things I am not quite sure about:
1)
Is this use of a generic SpecBuilder a clean design?

2)
Is there a way to avoid writing those repository interfaces for each entity? Let's say a generic repository?

3)
The MyQueryDescriptor class has a method to return an instance of an Entity, which will be queried.
What would be a clean way to obtain the according repository based on the entity class, avoiding a switch case? I was thinking about putting an annotation with the specific repository class to each entity but it feels a bit smelly.
Should I create a factory and inject a map like

Entity1.class => Entity1DAO
Entity2.class => Entity2DAO

?

Upvotes: 7

Views: 20355

Answers (2)

crizzis
crizzis

Reputation: 10716

Is this use of a generic SpecBuilder a clean design?

Depends what criteria you have for clean design. Will the same MyQueryDescriptor work for different entities? Surely they have different properties, so you need to ask yourself whether a given MyQueryDescriptor could be mistakenly used for an incompatible entity and ways in which you could prevent it. We cannot comment on that since we don't know how your SpecBuilder works.

Is there a way to avoid writing those repository interfaces for each entity? Let's say a > generic repository?

Nope. It's not much boilerplate either, though.

  1. The MyQueryDescriptor class has a method to return an instance of an Entity, which will be queried. What would be a clean way to obtain the according repository based on the entity class, avoiding a switch case?

I suppose you could use getBeanProvider at runtime, where you would define resolvableType as CrudRepository<MyEntityType, IdType>.

However, if I were you, I'd consider switching to using JPA Criteria API without the JpaSpecificationExecutor abstraction on top of it. That would probably prove to be more natural. The design of Spring repositories is centered around the idea of the repository organizing queries around the given specific entity, whereas your use case seems to go in exactly the opposite direction - to dynamically pick an entity and then find a repository to fit in, just to satisfy Spring's restrictions. You seem to be fighting the framework in that regard.

Upvotes: 0

asm0dey
asm0dey

Reputation: 2931

You can use entity inheritance and use Spring Expression Language (SpEL) to make repository issue calls on right entities. Like in my last update here

Upvotes: 2

Related Questions