How to Create Two Entities in JPA with the Same Database Table
A basic concept of JPA is that for each database table we create an entity. However, JPA can do much more, and in this article, I will show how to create two entities in JPA that share the same database table.
Scenario
We have a system which contains a table called bank-account, which contains some fields related to a checking account. This table is mapped to a java class called CheckingAccount, which is annotated with @Entity.
The CheckingAccount entity class code will be like this (lets use lombok to reduce the boilerplate code):
@Entity
@Table(name = "bank-account")
@Getter
@Setter
@NoArgsConstructor
public class CheckingAccount {
@Id
private int id;
private String name;
@Column("start-date")
private LocalDate startDate;
}
Remarks:
- The Entity annotation identifies that it is managed by the JPA.
- The Table annotation identifies the properties of the database table; in this case, we set the name of the database table.
- The start-date column of the database table was mapped to the startDate attribute in the Java class via Column annotation.
New Requirement
Now our system has a new requirement:
The system must support savings accounts, which have basically the same properties as the checking account.
The database administrator of your system decides to reuse the bank-account table and add a new field on this table, which will be used to differentiate between checking account and saving account.
This new field is called type and will have the values: C for checking account and S for saving account.
Solution
Then we have one database table and two entities:
The entities classes will be like this:
@Entity
@Table(name = "bank-account")
@Getter
@Setter
@NoArgsConstructor
public class CheckingAccount {
@Id
private int id;
private String name;
private String type;
@Column("start-date")
private LocalDate startDate;
}
@Entity
@Table(name = "bank-account")
@Getter
@Setter
@NoArgsConstructor
public class SavingAccount {
@Id
private int id;
private String name;
private String type;
@Column("start-date")
private LocalDate startDate;
}
This solution works fine; however, it has a consequence: Since the database is the same for both entities, when we query the CheckingAccount and SavingAccount, I have to add a filter condition on the type field because JPA does not know how to differentiate between these two entities in the database. In other words, when we make a query for a CheckingAccount entity, we have to explicitly add a filter to ensure that the field type is equal to “C”.
There is a smart way to solve this issue, which is to use the @Where annotation. When we add this annotation to the entity, JPA automatically adds the condition defined in the annotation to the queries. Let’s see how it looks:
@Entity
@Table(name = "bank-account")
@Where(clause = "type = \"C\"")
@Getter
@Setter
@NoArgsConstructor
public class CheckingAccount {
@Id
private int id;
private String name;
private String type;
@Column("start-date")
private LocalDate startDate;
}
@Entity
@Table(name = "bank-account")
@Where(clause = "type = \"S\"")
@Getter
@Setter
@NoArgsConstructor
public class SavingAccount {
@Id
private int id;
private String name;
private String type;
@Column("start-date")
private LocalDate startDate;
}
With this small change, we do not need to worry about queries because JPA will handle them for us.
We can notice that both entities have pretty much the same code; we could simplify it by using a mapped superclass:
Then the entities will be like this:
@MappedSuplerClass
@Getter
@Setter
@NoArgsConstructor
public class BankAccount {
@Id
private int id;
private String name;
private String type;
@Column("start-date")
private LocalDate startDate;
}
@Entity
@Table(name = "bank-account")
@Where(clause = "type = \"C\"")
@Getter
@Setter
@NoArgsConstructor
public class CheckingAccount extends BankAccount {
}
@Entity
@Table(name = "bank-account")
@Where(clause = "type = \"S\"")
@Getter
@Setter
@NoArgsConstructor
public class SavingAccount extends {
}
In conclusion, by implementing the @Where annotation, we’ve efficiently resolved the issue of differentiating between entities sharing the same database table.