avatarRenan Schmitt

Summary

The article discusses a technique for mapping two JPA entities to a single database table, specifically for managing checking and savings accounts within the same table.

Abstract

The article addresses the challenge of representing two distinct entities, checking and savings accounts, within a single database table in a Java Persistence API (JPA) context. Initially, the system only had a CheckingAccount entity mapped to the bank-account table. A new requirement necessitated the inclusion of savings accounts, which led to the addition of a type field in the existing table to distinguish between account types. The solution involved creating a SavingAccount entity in addition to the CheckingAccount entity, both mapped to the same bank-account table. To ensure proper differentiation during queries, the @Where annotation was utilized to automatically filter results based on the type field. Furthermore, the article suggests refactoring the common attributes of both entities into a mapped superclass, BankAccount, to reduce code duplication and maintain simplicity.

Opinions

  • The author implies that JPA's flexibility allows for advanced mapping scenarios beyond the conventional one-to-one entity-to-table relationship.
  • It is suggested that using the @Where annotation is a "smart way" to handle queries for entities that share the same table, as it automates the filtering process based on a specific field.
  • The author values code reuse and maintainability, as evidenced by the recommendation to use a mapped superclass to avoid redundancy in entity definitions.
  • The use of Project Lombok's annotations (@Getter, @Setter, @NoArgsConstructor) is endorsed for reducing boilerplate code in entity classes.

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.

Java
Jpa
Tips And Tricks
Programming
Recommended from ReadMedium