Best way of handling entities inheritance in Spring Data JPA

Andrea picture Andrea · Dec 18, 2014 · Viewed 32k times · Source

I've three JPA entity classes A, B and C with the following hierarchy:

    A
    |
+---+---+
|       |
C       B

That is:

@Entity
@Inheritance
public abstract class A { /* ... */ }

@Entity
public class B extends A { /* ... */ }

@Entity
public class C extends A { /* ... */ }

Using Spring Data JPA, what is the best way to write repositories classes for such entities?

I know that I can write these:

public interface ARespository extends CrudRepository<A, Long> { }

public interface BRespository extends CrudRepository<B, Long> { }

public interface CRespository extends CrudRepository<C, Long> { }

but if in the class A there is a field name and I add this method in the ARepository:

public A findByName(String name);

I've to write such method also in the other two repositories, and this is a bit annoying.. Is there a better way to handle such situation?

Another point I would like to have is that ARespository should be a read-only repository (i.e. extend the Repository class) while the other two repositories should expose all the CRUD operations.

Let me know possible solutions.

Answer

Andrea picture Andrea · Dec 18, 2014

I used the solution also described in this post from Netgloo's blog.

The idea is to create a generic repository class like the following:

@NoRepositoryBean
public interface ABaseRepository<T extends A> 
extends CrudRepository<T, Long> {
  // All methods in this repository will be available in the ARepository,
  // in the BRepository and in the CRepository.
  // ...
}

then I can write the three repositories in this way:

@Transactional
public interface ARepository extends ABaseRepository<A> { /* ... */ }

@Transactional
public interface BRepository extends ABaseRepository<B> { /* ... */ }

@Transactional
public interface CRepository extends ABaseRepository<C> { /* ... */ }

Moreover, to obtain a read-only repository for ARepository I can define the ABaseRepository as read-only:

@NoRepositoryBean
public interface ABaseRepository<T> 
extends Repository<T, Long> {
  T findOne(Long id);
  Iterable<T> findAll();
  Iterable<T> findAll(Sort sort);
  Page<T> findAll(Pageable pageable);
}

and from BRepository extend also the Spring Data JPA's CrudRepository to achieve a read/write repository:

@Transactional
public interface BRepository 
extends ABaseRepository<B>, CrudRepository<B, Long> 
{ /* ... */ }