Enterprise Java

Using Enum in Spring Data JPA Queries

When constructing our persistence layer using Spring Data JPA, we frequently deal with entities that contain enum fields. These enum fields represent a predefined set of constants, such as order statuses, user roles, or the stages of an article in a publishing system. Querying entities based on these enum fields is a common necessity, and Spring Data JPA offers several methods to achieve this.

Let us delve into understanding how Spring Data JPA handles enums. Enums in Spring Data JPA are used to represent a predefined set of constants, such as order statuses, user roles, or the stages of an article in a publishing system. Querying entities based on their enum fields is a common requirement, and Spring Data JPA provides several ways to accomplish this, ensuring that enums are efficiently managed and utilized within your persistence layer.

1. Introduction

Spring Data JPA is a part of the larger Spring Data family, which aims to simplify the implementation of data access layers by reducing the amount of boilerplate code required. It provides a consistent approach to data access while supporting a wide range of relational and non-relational databases. Spring Data JPA specifically focuses on JPA (Java Persistence API) as the persistence standard. It offers powerful abstractions and repository support, allowing developers to easily create, read, update, and delete records without writing explicit SQL queries.

Native queries in Spring Data JPA refer to the use of raw SQL queries directly within the repository interfaces. While Spring Data JPA’s derived and custom queries provide a high level of abstraction, there are situations where more complex or performance-optimized queries are required. Native queries offer a way to leverage the full power of SQL and database-specific features, ensuring that developers can execute highly specialized queries when necessary. These queries can be defined using the @Query annotation, providing flexibility and control over database interactions.

2. Setting up a database on Docker

Usually, setting up the database is a tedious step but with Docker, it is a simple process. You can watch the video available at this link to understand the Docker installation on Windows OS. Once done open the terminal and trigger the below command to set and run postgresql.

-- Remember to change the password –
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD= --name postgres postgres

-- command to stop the Postgres docker container --
docker stop postgres

-- command to remove the Postgres docker container --
docker rm postgres

Remember to enter the password of your choice. If everything goes well the postgresql database server will be up and running on a port number – 5432 and you can connect with the Dbeaver GUI tool to connect to the server.

Fig. 1. Postgres on Docker

2.1 Setting up pre-requisite data

To proceed further with the tutorial we will set up the required mock data in the postgresql.

create database testdb;

create table person (
    id serial primary key,
    name varchar(255) not null,
    status varchar(50) not null
);

INSERT INTO person (name, status) VALUES ('John Doe', 'ACTIVE');
INSERT INTO person (name, status) VALUES ('Jane Doe', 'INACTIVE');
INSERT INTO person (name, status) VALUES ('Jim Beam', 'PENDING');

select * from person;
Fig. 2: Mock person data

3. Code Example

3.1 Dependencies

Add the following dependencies to your build.gradle file or if you have created a spring project from start.spring.io this won’t be necessary as the file will be automatically populated with the dependencies information.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.1'
    id 'io.spring.dependency-management' version '1.1.5'
}

group = 'jcg'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'org.postgresql:postgresql'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

3.2 Configure application and database properties

Add the following properties to the application.properties file present in the resources folder.

# Spring application
spring.application.name=springjpaemum
server.port=9100

# Spring datasource configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=org.postgresql.Driver

# JPA/Hibernate properties
spring.jpa.hibernate.ddl-auto=none

# Show and format sql statements
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# Specify the dialect for PostgresSQL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

The properties file defines:

  • Spring application configuration:
    • spring.application.name=springjpaemum – Sets the name of the Spring application to “springjpaemum”.
    • server.port=9100 – Configures the application to run on port 9100.
  • Spring datasource configuration:
    • spring.datasource.url=jdbc:postgresql://localhost:5432/testdb – Specifies the URL of the PostgreSQL database located on localhost at port 5432 with the database name “testdb”.
    • spring.datasource.username=your_username – Sets the username for connecting to the database.
    • spring.datasource.password=your_password – Sets the password for the database connection.
    • spring.datasource.driver-class-name=org.postgresql.Driver – Defines the driver class name for PostgreSQL.
  • JPA/Hibernate properties: spring.jpa.hibernate.ddl-auto=none – Disables automatic schema generation or updating by Hibernate.
  • Show and format SQL statements:
    • spring.jpa.show-sql=true – Enables the logging of SQL statements executed by JPA.
    • spring.jpa.properties.hibernate.format_sql=true – Format the SQL statements to make them more readable in the logs.
  • Specify the dialect for PostgreSQL: spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect – Sets the Hibernate dialect to PostgreSQL, optimizing Hibernate’s SQL generation for PostgreSQL.

3.3 Creating the Model Class

Create a User entity class to interact with the JpaRepository interface and perform the SQL operations. The User entity indicates that the status field is an enumeration and should be persisted as a string in the database.

enum Status {

    ACTIVE, INACTIVE, PENDING
}

@Entity
@Table(name = "person")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @Enumerated(EnumType.STRING)
    private Status status;

    // Getters and setters
}

3.4 Creating the Data Interaction Layer

Create the book repository interface to interact with the book entity for interacting with the SQL table via the JpaRepository interface and perform the SQL operations.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    List<User> findByStatus(Status status);

    @Query(value = "SELECT * FROM person WHERE status = ?1", nativeQuery = true)
    List<User> findByStatusNative(String status);
}

The code defines:

  • @Repository: Marks the interface as a Spring Data repository, which will be treated as a Spring-managed bean and provide data access operations.
  • public interface UserRepository extends JpaRepository<User, Long>: Defines a public interface named UserRepository that extends JpaRepository. JpaRepository<User, Long>: Specifies that this repository manages User entities with Long type primary keys. Provides CRUD operations and additional methods to interact with the database.
  • List<User> findByStatus(Status status);: Declares a method to find users by their status. Spring Data JPA will automatically implement this method based on the method name, allowing you to retrieve users with a specific status.
  • List<User> findByStatusNative(String status);: Declares a method that uses the custom native query defined above. Retrieves users based on the provided status string using the native SQL query.
  • @Query(value = "SELECT * FROM person WHERE status = ?1", nativeQuery = true): Defines a custom query using the @Query annotation. nativeQuery = true: Indicates that the query is a native SQL query rather than a JPQL (Java Persistence Query Language) query.

3.5 Create the Controller file

Now, let’s create a REST controller to handle client operations:

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserRepository userRepository;

    @Autowired
    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    //http://localhost:9100/users/status/{{status}}
    @GetMapping("/status/{status}")
    public List<User> getUsersByStatus(@PathVariable Status status) {
        return userRepository.findByStatus(status);
    }

    //http://localhost:9100/users/status-native/{{status}}
    @GetMapping("/status-native/{status}")
    public List<User> getUsersByStatusNative(@PathVariable String status) {
        return userRepository.findByStatusNative(status);
    }

    // Note: service layer class skipped for brevity.
}

The code defines:

  • public UserController(UserRepository userRepository): Defines a constructor that takes a UserRepository as a parameter and assigns it to the userRepository field.
  • @GetMapping("/status/{status}"): Maps HTTP GET requests with the URL path /status/{status} to the getUsersByStatus method. {status} is a placeholder for a path variable that will be passed to the method.
  • @GetMapping("/status-native/{status}"): Maps HTTP GET requests with the URL path /status-native/{status} to the getUsersByStatusNative method. {status} is a placeholder for a path variable that will be passed to the method.

3.6 Create the Main file

Create a Spring boot application to initialize the application and hit the controller endpoints.

@SpringBootApplication
public class SpringjpaemumApplication {

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

3.7 Run the application

Run your Spring Boot application and the application will be started on a port number specified in the application properties file. As soon as the application is started the application endpoints will be initialized and you can use the endpoints to interact with the database to fetch the details.

-- GET
http://localhost:9100/users/status/{{status}}

-- GET
http://localhost:9100/users/status-native/{{status}}

4. Conclusion

In conclusion, building a persistence layer with Spring Data JPA allows for efficient management of entities that contain enum fields, which represent a fixed set of constants such as order statuses, user roles, or article stages in a publishing system. Querying these entities based on their enum fields is a frequent requirement, and Spring Data JPA offers multiple methods to effectively handle this need, ensuring that applications can leverage the power of enums to maintain clear and concise data structures.

5. Download the source code

Download
You can download the full source code of this example here: Using Enum in Spring Data JPA Queries

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