3

I have an abstract entity annotated with @MappedSuperclass:

@MappedSuperclass
public abstract class BaseEntity {
    public abstract T getId();
    public abstract void setId(T id);
}

Then I inherit my Entities from it, defining their id in each one:

@Entity
public class EntityA {
  @Id
  private int id;
  // ....
}

@Entity
public class EntityB {
  @Id
  private long id;
  // ....
}

Now I want to create a generic JpaRepository that accepts any class that extends from my Base Entity:

public interface BaseRepository<T extends BaseEntity, ID extends Serializable> extends JpaRepository<T, ID> {
}

But Spring trows an exception saying BaseEntity has no ID:

java.lang.IllegalArgumentException: This class [BaseEntity] does not define an IdClass

Please, check Plog's comments in his answer. I could solve it injecting each repository type in service's contructor

Cœur
  • 37,241
  • 25
  • 195
  • 267
gmc
  • 3,910
  • 2
  • 31
  • 44

1 Answers1

8

As far as I know you can't make generic repositories like this. You will need to make an individual repository for each of your concrete entity classes. aggregate roots (thanks @JensSchauder).

You can however make a generic base repository that can define some common queries between these two repositories by marking it as a @NoRepositoryBean:

@NoRepositoryBean
public interface BaseRepository<T extends BaseEntity, ID extends Serializable> extends JpaRepository<T, ID> {
  //common methods
}

Your concrete class repositories should then extend this instead of JpaRepository:

public interface EntityARepository extends BaseRepository<EntityA, Integer> {
}
Plog
  • 9,164
  • 5
  • 41
  • 66
  • One minor complaint: You don't make a repository for each concrete entity class, but for each Aggregate Root. See https://stackoverflow.com/a/38542469/66686 – Jens Schauder Nov 10 '17 at 11:12
  • But I need that generic repository, because I want to create a generic service with the generic repo autowired so that Spring automatically injects the repo for the corresponding entity – gmc Nov 10 '17 at 11:13
  • @gmc I'm not sure how that would work. Would you have two concrete services that extend the generic service? – Plog Nov 10 '17 at 11:20
  • Exactly. If I had all my entities having similar PKs I had it working, but I have 3 kinds of PKs: Long id, Long other_name and composite. I'm trying to make it all generic, but Its hard to account for such heterogeneity – gmc Nov 10 '17 at 11:22
  • 1
    @gmc You can define the `@NoRepositoryBean` BaseRepository as a property of your BaseService but don't autowire it in. Instead autowire the specific repository to each concrete service via constructor injection. Similar to my answer here: https://stackoverflow.com/questions/47201009/springframework-expected-single-matching-bean-but-found-2/47201232#47201232 – Plog Nov 10 '17 at 11:25
  • Seems like a good idea ... I'm gonna give it a try, I'll let you know. Thanks anyway – gmc Nov 10 '17 at 11:29
  • 3
    Hey @Plog, it's working. I love you. I'm marking your answer as correct and referring to the comments. Thank you so much – gmc Nov 10 '17 at 11:42
  • No problem. Im glad I could help :) good luck with the rest of your project. – Plog Nov 10 '17 at 11:43