50

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.

Andrea
  • 15,900
  • 18
  • 65
  • 84
  • You could write public abstract A findByName(String name), so all subclasses have to implement this method. – sven.kwiotek Dec 18 '14 at 14:53
  • @s.kwiotek but I don't want to implement such method (Spring Data JPA do it for me, with some magic :) ).. I would define it only in one place, like in the `ARespository`.. – Andrea Dec 18 '14 at 14:58
  • One thing I've found with repositories is that *inheritance in the entities does not dictate inheritance in the repositories*. Whereas a Banana is 'a' Fruit, a BananaBox is not a FruitBasket. So to speak. I've had more success having the BananaRepo *use* a FruitRepo through *composition*, accessing it merely for **(A)** the `SELECT` part of the query, and **(B)** populating the base Fruit-properties of the Banana that was just instantiated. You'd have to check if this is possible with Spring, though. – Timo Sep 08 '17 at 07:35

1 Answers1

65

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> 
{ /* ... */ }
Andrea
  • 15,900
  • 18
  • 65
  • 84
  • 1
    In this example can you use use repository B to set attributes belonging to Class A that is extended by Class B? I wanted to use a system like this to update B via a spring-data-rest controller. – ALM Jun 03 '16 at 04:14
  • @ALM I'm pretty sure you can do it. Did you tried it? – Andrea Jun 03 '16 at 14:24
  • Yes I am about to try right now. I am actually debating how I want to set it up but I think I will create a very simple test first then run that to see. I read the other article and after that I believe it should inherit what is for repository A and then be able to be used by B. A would also be setup as read-only – ALM Jun 03 '16 at 15:09
  • 1
    @ALM did you find a proper solution? I am also having a similar case where I want the child class repos for writes and the parent class repo just for reads such as GET ALL (including child entities). I also want to do it the way spring-data-rest does it, is there a way? – Soumitri Pattnaik Jan 20 '20 at 09:21