Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Repository with Inheritance.JOINED

I have designed the entities in my application to follow the Hibernate's inheritance strategy Inheritance.JOINED.

The base abstract class is UserTable and the concrete derived classes are ClientTable and the OwnerTable.

The thing that I want to achieve is to have a single repository in which I could find any UserTable (both ClientTable and OwnerTable) by the Id or Email. The important requirement is that they can be cast to the correct type once they are fetched, and that they carry their specific fields (and not just the ones inherited from UserTable).

Equally important, I should be able to persist those entities through the same repository, ie. repository.save(clientTable).

So something like this should be possible:

UserTableRepository repository = // ...

int clientId = 3; 
int ownerId = 5;

ClientTable clientTable = (ClientTable) repository.findByUserId(clientId);
OwnerTable clientTable = (OwnerTable) repository.findByUserId(ownerId);

clientTable = (ClientTable) repository.findByEmail(clientEmail);
ownerTable = (OwnerTable) repository.findByEmail(ownerEmail);

My entities look like this.

UserTable.java

@Entity
@Table(name = "USER")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class UserTable implements Serializable {

    @Column(name = "USER_ID", nullable = false)
    private Long userId;

    @EmbeddedId
    private UserTablePK userTablePK;

    @Column(name = "PASSWORD", nullable = false, length = 512)
    private String password;

    @Column(name = "FIRSTNAME", nullable = false, length = 256)
    private String firstName;

    // get, set
}

.. and a primary key

UserTablePK.java

@Embeddable
public class UserTablePK implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "EMAIL", nullable = false, length = 256)
    private String email;

    public UserTablePK() {
    }

    public UserTablePK(String email) {
        this.email = email;
    }

    // get, set
}

ClientTable.java

@Entity
@Table(name = "CLIENT")
public class ClientTable extends UserTable implements Serializable {

    @Column(name = "SHOPING_POWER")
    private Integer shoppingPower;

    @Column(name = "SHOPPING_FREQUENCY")
    private Integer shoppingFreq;
    
    // get, set
}

OwnerTable.java

@Entity
@Table(name = "OWNER")
public class OwnerTable extends UserTable implements Serializable {

    @Column(name = "EFFICIENCY")
    private String efficiency;

    @Column(name = "SALARAY")
    private Integer;

    // get, set
}

Now once the entities are set, I need to write a repository that would act like described above, and here is where I need some help. I was thinking to write something like this.

UserTableRepository.java

@Repository
public interface UserTableRepository extends CrudRepository<UserTable, UserTablePK> {

    @Query("SELECT e FROM UserTable e WHERE e.userTablePK.email = ?1")
    public UserTable findByEmail(String email); // if owner email, retrieve OwnerTable; if client email, retrieve ClientTable instance

    @Query("SELECT e FROM UserTable e WHERE e.userId = ?1")
    public UserTable findByUserId(Long userId); // if owner id, retrieve OwnerTable; if client id, retrieve ClientTable instance
}

I didn't actually try this yet, because I want to check if this is even the right route to take, but: I am rather skeptical that this Query would work. Because in order to retrieve the whole subclass object, some kind of JOIN should be used, and the problem is that I can't explicitly say ie. JOIN ClientTable because then I would not be able to fetch any OwnerTable entities.

How can I achieve that I can fetch both subclasses from the same repository?

UPDATE

Also, the requirement is to be able to fetch for the specific subclass. So something like:

List<ClientTable> clients = repository.findAllClients();

Is that possible using the same repository, or should I write repositories specific for subclasses? For example.

@Repository
public interface ClientTableRepository extends CrudRepository<ClientTable, ClientTablePK> {
 // empty
}

... and then call them like:

ClientTableRepository repository = // ...

List<ClientTable> clients = repository.findAll();

Would that be enough for the JPA to determine how to find all the instances of just a specific subclass?

like image 658
wesleyy Avatar asked Feb 09 '18 08:02

wesleyy


1 Answers

What you want to achieve is exactly how inheritance in JPA works.

So your queries are perfectly fine and you will have instances of the subclasses in the result list.

Read more about JPA inheritance in one of my blog posts:

https://72.services/inheritance-in-jpa/

like image 50
Simon Martinelli Avatar answered Sep 30 '22 15:09

Simon Martinelli