Enterprise Java

Query JPA Single Table Inheritance

In Java Persistence API (JPA), inheritance mapping provides a way to map Java class hierarchies to database tables. Single Table Inheritance is one of the inheritance strategies where all entities in a class hierarchy are stored in a single database table. Although simple to implement, it requires a discriminator column to distinguish between various subclasses. Let us delve into understanding JPA inheritance with single table strategy, exploring how it allows different entity types to be stored in a single database table while distinguishing them using a discriminator column.

1. Single Table Inheritance SubTypes

Single Table Inheritance is managed by using the @Inheritance annotation with InheritanceType.SINGLE_TABLE. In this approach, a single table is created to store all fields of both the superclass and subclasses. A discriminator column is used to identify the type of entity being stored.

2. Code Example

2.1 Setting up the Project and adding dependencies

We’ll start by creating a Spring Boot project with the help of Spring Initializr and add the necessary dependencies. In the pom.xml file, add the following dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.2 Setting up the Mock data

We will be using a PostgreSQL database running on Docker. Once the PostgreSQL database is up and running on Docker use the below SQL commands to create the mock data.

-- Create Database
CREATE DATABASE test;

-- Create Employee Table with Single Table Inheritance
CREATE TABLE employee (
    id BIGSERIAL PRIMARY KEY,            -- Unique identifier with auto-increment (BIGSERIAL)
    name VARCHAR(100) NOT NULL,          -- Common attribute for all employees
    salary DOUBLE PRECISION,             -- Specific to FullTimeEmployee
    hourly_rate DOUBLE PRECISION,        -- Specific to PartTimeEmployee
    employee_type VARCHAR(20) NOT NULL   -- Discriminator column (FULL_TIME, PART_TIME)
);

-- Insert FullTimeEmployee
INSERT INTO employee (name, salary, employee_type) VALUES ('John Doe', 60000, 'FULL_TIME');
INSERT INTO employee (name, salary, employee_type) VALUES ('Emily Carter', 75000, 'FULL_TIME');

-- Insert PartTimeEmployee
INSERT INTO employee (name, hourly_rate, employee_type) VALUES ('Jane Smith', 15, 'PART_TIME');
INSERT INTO employee (name, hourly_rate, employee_type) VALUES ('Tom Brown', 18, 'PART_TIME');

2.3 Define the Java Subclasses

Let’s start with defining the base class: Employee with shared properties (id and name) for the subclasses.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type", discriminatorType = DiscriminatoryType.STRING)
public abstract class Employee {

    private Long id;

    private String name;

    // Constructors, Getters, and Setters
}

Now, we will define two subclasses: FullTimeEmployee and PartTimeEmployee. Each will have its unique properties followed by the setter and getter methods. In both the below classes, employee_type is the discriminator column, which will contain FULL_TIME for full-time employees and PART_TIME for part-time employees.

2.3.1 FullTimeEmployee Model class

@Entity
@DiscriminatorValue("FULL_TIME")
public class FullTimeEmployee extends Employee {
    private double salary;

    // Constructors, Getters, and Setters
}

The given code defines a subclass of Employee called FullTimeEmployee, which is an entity in a Java Persistence API (JPA) context.

  • @DiscriminatorValue("FULL_TIME"): This annotation is used in conjunction with single table inheritance. It specifies the value that will be stored in the discriminator column of the table to distinguish between different types of employees. In this case, the discriminator value “FULL_TIME” will be used to identify instances of FullTimeEmployee in the database table.

2.3.2 PartTimeEmployee Model class

@Entity
@DiscriminatorValue("PART_TIME")
public class PartTimeEmployee extends Employee {
    private double hourlyRate;

    // Constructors, Getters, and Setters
}

The provided code defines a subclass of Employee called PartTimeEmployee, which is a JPA entity.

  • @DiscriminatorValue("PART_TIME"): This annotation is used when implementing Single Table Inheritance in JPA. It specifies that the value “PART_TIME” will be stored in the discriminator column of the database table for rows representing PartTimeEmployee instances. This allows the system to distinguish between different types of employees, such as full-time or part-time, stored in the same table.

2.4 Querying with JPA Repository

Spring Data JPA allows us to query entities using the repository pattern. We will create a repository interface for Employee and then demonstrate how to query for specific subtypes.

2.4.1 Define the EmployeeRepository

Create an interface EmployeeRepository extending JpaRepository to provide CRUD (Create, Read, Update, and Delete) operations.

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
    List<FullTimeEmployee> findBySalaryGreaterThan(double salary);
    List<PartTimeEmployee> findByHourlyRateLessThan(double hourlyRate);
}

2.4.2 Testing Queries in Service Layer

We will add a service class, EmployeeService, to handle the querying logic for each subtype. Here, we can inject EmployeeRepository and use the specific queries based on the subclass.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;

    public List<FullTimeEmployee> getHighSalaryEmployees(double salary) {
        return employeeRepository.findBySalaryGreaterThan(salary);
    }

    public List<PartTimeEmployee> getLowHourlyRateEmployees(double hourlyRate) {
        return employeeRepository.findByHourlyRateLessThan(hourlyRate);
    }
}

2.4.3 Controller Layer to Test API Endpoints

Now, we’ll add a controller to expose REST endpoints for these queries.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/full-time/high-salary")
    public List<FullTimeEmployee> getHighSalaryEmployees(@RequestParam double salary) {
        return employeeService.getHighSalaryEmployees(salary);
    }

    @GetMapping("/part-time/low-hourly-rate")
    public List<PartTimeEmployee> getLowHourlyRateEmployees(@RequestParam double hourlyRate) {
        return employeeService.getLowHourlyRateEmployees(hourlyRate);
    }
}

2.5 Setting up the application properties

Add the following code to the application.properties file.

spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=your_username
spring.datasource.password=your_password

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

2.6 Create a Main class

Add the following code to the main class that act as the entry point to the Spring boot application.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class EmployeeApplication {

    public static void main(String[] args) {
        SpringApplication.run(EmployeeApplication.class, args);
    } 
}

3. Run the application

Run the code by running the EmployeeApplication.java and trigger the api endpoints.

-- GET /employees/full-time/high-salary?salary=50000
[
    {
        "id": 1,
        "name": "John Doe",
        "salary": 60000.0,
        "employee_type": "FULL_TIME"
    },
    ...
]

-- GET /employees/part-time/low-hourly-rate?hourlyRate=20
[
    {
        "id": 2,
        "name": "Jane Smith",
        "hourlyRate": 15.0,
        "employee_type": "PART_TIME"
    },
    ...
]

4. Conclusion

Using Single Table Inheritance in JPA can simplify the database structure by storing different entity types in a single table. With Spring Data JPA, querying by specific subtypes is straightforward. This approach is efficient for handling entities with shared attributes and enables easier database management. However, when there are a lot of subtype-specific fields, other inheritance strategies might be better suited. This example provides a complete overview of implementing and querying Single Table Inheritance with JPA in a Spring Boot application.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button