Spring Boot + JPA | Java Persistence API : A Practical Guide

When building database-driven applications with Spring Boot, Java Persistence API (JPA) simplifies data access by abstracting away the complexities of SQL and providing a seamless way to interact with relational databases. Spring Boot integrates JPA with minimal configuration, leveraging powerful tools like Hibernate for object-relational mapping (ORM). This practical guide covers JPA essentials, from entity mapping to custom queries, while showcasing how to implement pagination and sorting for better data management.

Table of Contents

  1. Entity Mapping Basics
  2. Repository Interfaces (CrudRepository, JpaRepository)
  3. Pagination and Sorting
  4. Custom Queries with @Query
  5. External Resources for Further Learning
  6. Final Thoughts

Entity Mapping Basics

What is Entity Mapping?

Entity mapping is the process of linking Java objects (entities) to relational database tables. This is foundational in JPA, allowing developers to manage database records as objects in their applications.

Entities are annotated with JPA-specific annotations, such as @Entity, @Table, @Id, and @Column, to define their relationship with database tables and columns.

Example

User.java

package com.example.demo.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Column;

@Entity
public class User {

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

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String email;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Explanation of Key Annotations

  1. @Entity
    Marks the class as a JPA entity, meaning it corresponds to a database table.
  2. @Id
    Specifies the primary key field of the table.
  3. @GeneratedValue
    Defines primary key generation strategy (e.g., IDENTITY for auto-increment).
  4. @Column
    Customizes the column properties, such as nullable, unique, or length.

Table Creation

When you run the application, JPA (with Hibernate) will use the above entity to create the User table in the connected database.

Generated SQL:

CREATE TABLE User (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    email VARCHAR(255) NOT NULL
);

Repository Interfaces (CrudRepository, JpaRepository)

Spring Boot provides repository interfaces to simplify database interactions. These repositories abstract data access layers and eliminate the need to write boilerplate code for common operations.

CrudRepository

The CrudRepository interface provides basic CRUD (Create, Read, Update, Delete) functionality for your entities. It is a generic interface and works with any entity type.

Example Repository:

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {
}

Available methods include:

  • save(S entity): Saves or updates the entity.
  • findById(ID id): Retrieves an entity by its ID.
  • findAll(): Returns all entities.
  • deleteById(ID id): Deletes an entity by ID.

JpaRepository

The JpaRepository interface extends CrudRepository and provides additional functionality specific to JPA, such as pagination and sorting.

Example Repository:

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

Additional methods:

  • findAll(Pageable pageable): Supports pagination.
  • findAll(Sort sort): Retrieves sorted data.
  • Built-in methods for advanced querying, like retrieving data based on conditions.

When to Use Which?

  • Use CrudRepository for simple CRUD operations.
  • Use JpaRepository when you need pagination, sorting, or custom query methods.

Pagination and Sorting

Large datasets are often impractical to load all at once. Pagination and sorting help efficiently manage and retrieve data.

Pagination Example

The Pageable interface is used to define page size, number, and sorting criteria.

Controller Method:

@GetMapping("/users")
public Page<User> getUsers(Pageable pageable) {
    return userRepository.findAll(pageable);
}

Request Example:

GET http://localhost:8080/users?page=0&size=5

Response:

  • Returns the first page of data with 5 users per page.

Sorting Example

To sort records, pass a Sort parameter:

@GetMapping("/users/sort")
public List<User> getUsersSorted(Sort sort) {
    return userRepository.findAll(sort);
}

Request Example:

GET http://localhost:8080/users/sort?sort=username,asc

Response:

  • Users sorted alphabetically by their username.

You can also combine pagination and sorting like this:

Pageable pageable = PageRequest.of(0, 5, Sort.by("username").ascending());
Page<User> page = userRepository.findAll(pageable);

Custom Queries with @Query

Sometimes, you need more advanced or optimized queries that cannot be expressed using the built-in methods. For such cases, Spring Data provides the @Query annotation to define custom JPQL (Java Persistence Query Language) or SQL queries.

Example Query

Retrieve users by username:

@Query("SELECT u FROM User u WHERE u.username = ?1")
Optional<User> findByUsername(String username);

Usage Example:

Optional<User> user = userRepository.findByUsername("john_doe");

Native SQL Queries

For complex requirements, you can also use native SQL:

@Query(value = "SELECT * FROM User WHERE email LIKE %?1%", nativeQuery = true)
List<User> findByEmailContaining(String keyword);

Parameters Example

You can use parameterized queries to avoid SQL injection:

@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

External Resources for Further Learning


Final Thoughts

Spring Boot and JPA simplify database-driven application development by providing robust abstractions for entity mapping, data repositories, and advanced querying. This guide has covered the foundational practices for effectively using Spring Boot with JPA, from defining entities to implementing custom queries.

By understanding how to leverage Spring Data repositories and JPA features like pagination, sorting, and mapping, you can develop efficient, maintainable, and scalable applications. Bookmark this guide as a reference for building JPA-powered applications in Spring Boot!

Similar Posts

Leave a Reply