Handling entities inheritance with Spring Data JPA

Suppose to have three JPA entities, say User, Person and Company, where the last two extends (inherits by) the first one:

       User
        |
  +-----+-----+
  |           |
Person     Company

that is:

@Entity
@Inheritance
public abstract class User { 
 
  @Id
  private long id;

  @NotNull
  private String email;

  // ...
}

@Entity
public class Person extends User { /* ... */ }

@Entity
public class Company extends User { /* ... */ }

We want to write three Repository classes (aka DAO), one for each entity, following the hierarchy structure so all repository’s methods referring the base class User (for example a method findByEmail) are available on all repositories but written only once.

So, we start writing a generic user repository class:

@NoRepositoryBean
public interface UserBaseRepository<T extends User> 
extends CrudRepository<T, Long> {

  public T findByEmail(String email);
 
}

All methods in this repository will be available in the UserRepository, in the PersonRepository and in the CompanyRepository.

Then we define repositories for the three entities:

@Transactional
public interface UserRepository extends UserBaseRepository<User> { /* ... */ }

@Transactional
public interface PersonRepository extends UserBaseRepository<Person> { /* ... */ }

@Transactional
public interface CompanyRepository extends UserBaseRepository<Company> { /* ... */ }

That’s all!

Some Tips

Referring the right entity type in the base repository

In the base repository you can referring the actual entity type inside a custom query (@Query annotation) using the #{#entityName} SpEL variable, for example:

public interface UserBaseRepository 
extends CrudRepository<T, Long> {

  @Query("select u from #{#entityName} as u where u.email = ?1 ")
  T findByEmail(String email);

}

The value of #{#entityName} will be the entity type T.

Read-only repository for User class

To obtain a read-only repository for the User class we can define the UserBaseRepository as read-only:

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

and from PersonRepository (and CompanyRepository) extend also the Spring Data JPA’s CrudRepository to achieve a read/write repository:

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

Try yourself

Get a working example using the code described above from our GitHub repository here:
https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-springdatajpa-inheritance

References

https://spring.io/blog/2011/07/27/fine-tuning-spring-data-repositories
http://docs.spring.io/spring-data/jpa/docs/current/reference/html

  • mrts

    Thanks for sharing!

  • Augusto Santos

    Thanks, it’s a perfect tutorial. I have a question. Can I replace CrudRepository by JpaRepository in the UserBaseRepository class? What’s the difference between them?

  • Данил Ходырев

    How I can get User by id?

  • Vishal Wagh

    Nice Article. How do i get report from multiple class. Lets say, i want retrieve data from User, Person & Company tables, using some custom queries & map to one of the custom bean.
    Please suggest some approach.

  • Armando Suárez Pons

    Dear Andrea, very good article, but polymorphism is not solved. When I try to create a child class tells me that the parent does not have that attribute

  • Roland

    Thanks for this great post!

    At “Read-only repository for User class” – I guess you should extend ReadOnlyRepository instead of Repository, isn’t it?

    • Roland

      Sorry, there is no ReadOnlyRepository :). Your exaple is correct.

  • Daniele Palladino

    Thanks for all, I have only one question for you.
    I have a custom query and I haven’t the dtype information. I must create a DTO Class and verify with an instanceof istruction to determinate the dtype?

  • suman biswas

    facing this issue
    org.springframework.data.mapping.PropertyReferenceException: No property findOne found for type

  • Achraf Smlali

    thank you for the tuto .
    after doing the same in my controller i called personRepository.findAll() to get all persons but in result i got also companies

Categories

Category BootstrapCategory CoffeescriptCategory DrupalCategory GravCategory HTMLCategory JavascriptCategory JoomlaCategory jQueryCategory LaravelCategory MagentoCategory PHPCategory SharePointCategory SpringCategory ThymeleafCategory WordPressCategory Workflow

Comments

Developed and designed by Netgloo
© 2019 Netgloo