© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
K. S. P. Reddy, S. UpadhyayulaBeginning Spring Boot 3https://doi.org/10.1007/978-1-4842-8792-7_8

8. Working with JPA

Siva Prasad Reddy Katamreddy1   and Sai Subramanyam Upadhyayula2
(1)
Hyderabad, India
(2)
Rotterdam, The Netherlands
 

The Java Persistence API (JPA) is an object-relational mapping (ORM) framework that’s part of the Java EE platform. JPA simplifies the implementation of the data access layer by letting developers work with an object-oriented API instead of writing SQL queries by hand. The most popular JPA implementations are Hibernate, EclipseLink, and OpenJPA.

The Spring Framework provides a Spring ORM module to integrate easily with ORM frameworks. You can also use Spring's declarative transaction management capabilities with JPA. In addition to the Spring ORM module, the Spring Data portfolio project provides a consistent, Spring-based programming model for data access to relational and NoSQL datastores.

Spring Data integrates with most of the popular data access technologies, including JPA, MongoDB, Redis, Cassandra, Solr, and ElasticSearch.

This chapter explores the Spring Data JPA, explains how to use it with Spring Boot, and looks into how you can work with multiple databases in the same Spring Boot application.

Introducing the Spring Data JPA

Chapter 1 discussed developing a web application using SpringMVC and JPA without Spring Boot. Without using Spring Boot, you must configure beans like DataSource, TransactionManager, and LocalContainerEntityManagerFactoryBean. You can use the Spring Boot JPA Starter spring-boot-starter-data-jpa to get up and running with JPA quickly. Let’s look at Spring Data JPA.

As noted in Chapter 5, Spring Data is an umbrella project that supports most of the popular data access technologies, including JPA, MongoDB, Redis, Cassandra, Solr, and ElasticSearch, in a consistent programming model. The Spring Data JPA is one of the modules for working with relational databases using JPA.

At times, you may need to implement data management applications to store, edit, and delete data. For those applications, you just need to implement CRUD (Create, Read, Update, Delete ) operations for entities. Instead of implementing the same CRUD operations again and again or rolling out your own generic CRUD DAO implementation, Spring Data provides various repository abstractions , such as CrudRepository, PagingAndSortingRepository, and JpaRepository. They provide out-of-the-box support for CRUD operations , pagination, and sorting.
public interface JpaRepository<T, ID> extends CrudRepository<T, ID>,PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
     @Override
     List<T> findAll();
     @Override
     List<T> findAll(Sort sort);
     @Override
     List<T> findAllById(Iterable<ID> ids);
     @Override
     <S extends T> List<S> saveAll(Iterable<S> entities);
     void flush();
     <S extends T> S saveAndFlush(S entity);
     <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
     void deleteAllInBatch(Iterable<T> entities);
     void deleteAllByIdInBatch(Iterable<ID> ids);
     void deleteAllInBatch();
     T getReferenceById(ID id);
     @Override
     <S extends T> List<S> findAll(Example<S> example);
     @Override
     <S extends T> List<S> findAll(Example<S> example, Sort sort);
}
The JpaRepository provides several methods for CRUD operations, along with the following interesting methods :
  • long count(); returns the total number of entities available.

  • boolean existsById(ID id); returns whether an entity with the given ID exists.

  • List<T> findAll(Sort sort); returns all entities sorted by the given options.

  • Page<T> findAll(Pageable pageable); returns a page of entities meeting the paging restriction provided in the Pageable object. This method can be found in the PagingAndSortingRepository.java, like so:

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {
     Iterable<T> findAll(Sort sort);
     Page<T> findAll(Pageable pageable);
}

The Spring Data JPA not only provides CRUD operations out of the box, it also supports dynamic query generation based on the method names.

For example,
  • By defining a User findByEmail(String email) method, Spring Data will automatically generate the query with a where clause, as in "where email = ?1".

  • By defining a User findByEmailAndPassword(String email, String password) method, Spring Data will automatically generate the query with a where clause, as in "where email = ?1 and password=?2".

Note

You can also use other operators such as OR, LIKE, Between, LessThan, LessThanEqual, and so on. Refer to http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation for a complete list of supporting operations.

But sometimes you may not be able to express your criteria using method names or the method names look ugly. Spring Data provides the flexibility to configure the query explicitly using the @Query annotation .
@Query("select u from User u where u.email=?1 and u.password=?2 and u.enabled=true")
User findByEmailAndPassword(String email, String password);
You can also perform data update operations using @Modifying and @Query, as follows :
@Modifying
@Query("update User u set u.enabled=:status")
int updateUserStatus(@Param("status") boolean status)

This example uses the named parameter status instead of the positional parameter ?1.

Using Spring Data JPA with Spring Boot

Now let’s add the Spring Data JPA support to your Spring blog project by adding the following dependencies to the pom.xml file:
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <scope>runtime</scope>
   </dependency>
</dependencies>

The spring-boot-starter-data-jpa dependency will auto-configure all the required beans to work with JPA in your application. You also added the dependency to download the MySQL Java Driver, which enables you to communicate with the MySQL database from your application.

The next step is making small changes in your domain classes so that Spring Boot understands how to persist them to the database. Listing 8-1 shows the Post.java class .
package com.apress.demo.springblog.domain;
import com.apress.demo.springblog.validation.BlogPostTitleAlreadyExists;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
@Entity
@Table
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@BlogPostTitleAlreadyExists
public class Post {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   @NotNull
   @Size(min = 3, max = 50, message = "Title must be minimum 3 characters, and maximum 50 characters long")
   private String title;
   @NotNull
   @Size(min = 3, max = 500, message = "Description must be minimum 3 characters, and maximum 500 characters long")
   private String description;
   @NotNull
   @Size(min = 3, max = 5000, message = "Body must be minimum 3 characters, and maximum 5000 characters long")
   private String body;
   private String slug;
   @Column(name = "post_status")
   private PostStatus postStatus;
   @Column(name = "created_on")
   private LocalDateTime createdOn;
   @Column(name = "updated_on")
   private LocalDateTime updatedOn;
   @OneToMany(mappedBy = "post",
         cascade = CascadeType.ALL,
         orphanRemoval = true)
   private List<Comment> comments;
}
Listing 8-1

JPA Entity Post.java

  • First, you add the @Entity and @Table annotations from JPA to denote that your Post class is a JPA Entity and Spring Data JPA should map it to the post table in the database.

  • You add @Id annotation on top of the id field to denote that it’s a primary key. You also add the @GeneratedValue annotation with strategy as AUTO, which will auto-increment the primary key whenever a new record is added to the table.

  • The @Column annotation can be added if you want to add a custom name for the column. If you don’t specify the annotation, the default name applies.

  • Lastly, you add the @OneToMany annotation on top of the comments fields to determine the one-to-many relationship between the Post and Comment objects. Similarly, you add the @ManyToOne annotation on the post field inside the Comment class to establish a bidirectional mapping between the Post and Comment objects.

  • You define the cascade attribute of the @OneToMany association as CascadeType.ALL with orphanRemoval=true. This means whenever a post is deleted, you also delete the related Comment records from the database.

package com.apress.demo.springblog.domain;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@Entity
@Table
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Comment {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;
   private String title;
   private String authorName;
   private String body;
   @Column(name = "created_on")
   private LocalDateTime createdOn;
   @Column(name = "updated_on")
   private LocalDateTime updatedOn;
   @ManyToOne(fetch = FetchType.LAZY)
   private Post post;
}

The @ManyToOne association uses the FetchType of LAZY, which offers better performance than EAGER fetching.

The next step is to refactor the PostRepository.java and CommentRepository.java classes to interfaces and extend them with the JpaRepository, as shown in Listings 8-2 and 8-3.
package com.apress.demo.springblog.repository;
import com.apress.demo.springblog.domain.Post;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Post, Long> {
   boolean existsByTitle(String title);
}
Listing 8-2

JPA Repository Interface PostRepository.java

package com.apress.demo.springblog.repository;
import com.apress.demo.springblog.domain.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CommentRepository extends JpaRepository<Comment, Integer> {
}
Listing 8-3

JPA Repository Interface CommentRepository.java

The JpaRepository interface provides many general-purpose database operation methods out of the box. But you can also define your methods and customize the repository according to your needs.

In Listing 8-2, you define a new method called existsByTitle. Spring Data JPA will parse the method name and understand that you want to check if the post exists or not with a given title.

Using the Sort and Pagination Features

Suppose you want to get all posts by title in ascending order. You can use the findAll(Sort sort) method as follows:
postRepository.findAll(Sort.by(Sort.Direction.ASC, "title"));
You can also apply sorting on multiple properties as follows:
postRepository.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "title"), new Sort.Order(Sort.Direction.DESC, "id")));

The posts will be ordered first by title in ascending order and then by ID in descending order.

In many web applications, you want to show data page by page. Spring Data makes it very easy to load data in the pagination style. Suppose you want to load the first 25 users on one page. You can use Pageable and PageRequest to get results by page as follows:
int size = 25;
int page = 0; //zero-based page index.
Pageable pageable = PageRequest.of(page, size);
Page<Post> postsPage = postRepository.findAll(pageable);
The postsPage will only contain the first 25 user records. You can get additional details, like the total number of pages, the current page number, whether there is a next page, whether there is a previous page, and more.
  • postsPage.getTotalElements(); returns the total amount of elements.

  • postsPage.getTotalPages(); returns the total number of pages.

  • postsPage.hasNext();

  • postsPage.hasPrevious();

  • List<Post> posts = postsPage.getContent();

You can also apply pagination along with sorting as follows:
Sort sort = new Sort(Direction.ASC, "title");
Pageable pageable = PageRequest.of(page, size, sort);
Page<Post> postsPage = postRepository.findAll(pageable);
Note

To learn more about Spring Data JPA, visit the official Spring Data JPA documentation at http://docs.spring.io/spring-data/jpa/docs/current/reference/html .

Summary

This chapter covered Spring Data JPA and its use with Spring Boot. The next chapter explains how to work with MongoDB in your Spring Boot applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.218.184