Florian Cramer
Florian Cramer

Reputation: 1358

Multiple base repositories in Spring Data JPA

I am currently migrating an application that manages JPA in form of Hibernate ORM by itself to Spring Boot 2.0.0 with Spring Data JPA 2.0.0. This application currently has about 50 entities, each with its own data source which I now want to migrate to Spring Data Repositories. Many of those data sources have many quite similar features, i.e. filtering with a custom filter class or fetching an entity by some sort of unique key that is not its id. I could very well just implement a custom base class as described in the Spring Data JPA Reference, but this would mean that I would have to would have to provide custom filters for each and every entity, where I only need on for maybe 10 of them.

Is there any possible way to have Spring Data merge multiple custom repository implementations as shown below?


Entities:

@Entity
public class Artist {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int id;

    @Column(unique = true)
    int secondaryId;
    String name;

    ... constructors, getter, setter, etc ...
}    

@Entity
public class Album {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int id;

    String name;

    ... constructors, getter, setter, etc ...
}

Custom repositories with default implementations:

public interface FilterRepository<T> {
    public T findByFilter(Filter<T> t);
}

public class FilterRepositoryImpl<T> implements AdditionalRepository<T> {
    public T findByFilter(Filter<T> t) {
        ... return something ...
    }
}

public interface FindByFieldRepository<T> {
    public T findByField(String fieldName, Object fieldValue);
}

public class FindByFieldRepositoryImpl<T> implements FindByFieldRepository<T> {
    public T findByField(String fieldName, Object fieldValue) {
        ... return something ...
    }
}

Spring Data Repositories

@Repository
public interface ArtistRepository extends JpaRepository<Artist, Integer>, 
        FilterRepository<Artist>, FindByFieldRepository<Artist> {
}

@Repository
public interface AlbumRepository extends JpaRepository<Album, Integer>, 
        FilterRepository<Album> {
}

Notice that ArtistRepository extends both custom repositories while AlbumRepository only extends FilterRepository. Another repository might extend JpaRepository but none of the custom defined ones.

Upvotes: 6

Views: 11496

Answers (1)

manish
manish

Reputation: 20135

This should be possible with two interfaces and a single custom repository implementation. For example:

Step 1: Simple extension of JpaRepository

@NoRepositoryBean
public interface FilteringJpaRepository<T> extends JpaRepository<T, Long> {
  T findByFilter(Filter<T> t);
}

Step 2: A more complicated extension of JpaRepository

@NoRepositoryBean
public interface ExtendedJpaRepository<T> extends FilteringJpaRepository<T> {
  T findByField(String fieldName, Object fieldValue);
}

Step 3: A custom repository implementation

Add a custom repository implementation as described in the documentation linked to in the question. Also make sure to change the JPA configuration to use the custom implementation.

@NoRepositoryBean
public class ExtendedJpaRepositoryImpl<T>
             extends SimpleJpaRepository<T, Long>
             implements ExtendedJpaRepository<T> {
  ...

  public T findByFilter(Filter<T> t) { ... }

  public T findByField(String fieldName, Object fieldValue) { ... }
}

Step 4: Extend from the appropriate repository as required

public interface AlbumRepository extends FilteringJpaRepository<Album> {}

public interface ArtistRepository extends ExtendedJpaRepository<Artist> {}

Since the functional repositories extend only the required repository, their consumers will only see methods appropriate for them.


Please see the sample app for this approach.

Upvotes: 4

Related Questions