From 7055bb3f6b190855e27f792e69efd479bd0303cf Mon Sep 17 00:00:00 2001
From: Manfred <77407079+manfred106@users.noreply.github.com>
Date: Wed, 7 Feb 2024 01:52:45 +0000
Subject: [PATCH] BAEL-7282: Getting all results at once in a paged query
method (#15761)
---
.../spring-boot-persistence-4/pom.xml | 13 +++
.../baeldung/paging/PagingApplication.java | 13 +++
.../main/java/com/baeldung/paging/School.java | 31 ++++++
.../java/com/baeldung/paging/Student.java | 42 +++++++
.../paging/StudentCustomQueryRepository.java | 14 +++
.../java/com/baeldung/paging/StudentDTO.java | 22 ++++
.../paging/StudentEntityGraphRepository.java | 13 +++
.../StudentNamedEntityGraphRepository.java | 13 +++
.../baeldung/paging/StudentRepository.java | 6 +
.../paging/StudentWithSchoolNameDTO.java | 24 ++++
.../StudentRepositoryIntegrationTest.java | 103 ++++++++++++++++++
.../test/resources/school-student-data.sql | 9 ++
12 files changed, 303 insertions(+)
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/PagingApplication.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/School.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/Student.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentCustomQueryRepository.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentDTO.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentEntityGraphRepository.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentNamedEntityGraphRepository.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentRepository.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentWithSchoolNameDTO.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/paging/StudentRepositoryIntegrationTest.java
create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/resources/school-student-data.sql
diff --git a/persistence-modules/spring-boot-persistence-4/pom.xml b/persistence-modules/spring-boot-persistence-4/pom.xml
index 223b754f07..13cb4d5b82 100644
--- a/persistence-modules/spring-boot-persistence-4/pom.xml
+++ b/persistence-modules/spring-boot-persistence-4/pom.xml
@@ -57,6 +57,17 @@
jackson-databind
${jackson.version}
+
+ org.modelmapper
+ modelmapper
+ ${modelmapper.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
@@ -76,6 +87,8 @@
1.0.7
3.7.0
2.16.0
+ 3.2.0
+ 1.18.30
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/PagingApplication.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/PagingApplication.java
new file mode 100644
index 0000000000..7a38ec5cd8
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/PagingApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.paging;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class PagingApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(PagingApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/School.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/School.java
new file mode 100644
index 0000000000..6c02cbe6dd
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/School.java
@@ -0,0 +1,31 @@
+package com.baeldung.paging;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Table(name = "school")
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = {"id"})
+public class School {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "school_id")
+ private Integer id;
+
+ private String name;
+
+}
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/Student.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/Student.java
new file mode 100644
index 0000000000..5cc20cd9af
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/Student.java
@@ -0,0 +1,42 @@
+package com.baeldung.paging;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.NamedAttributeNode;
+import jakarta.persistence.NamedEntityGraph;
+import jakarta.persistence.Table;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Entity
+@Table(name = "student")
+@NamedEntityGraph(name = "Student.school", attributeNodes = @NamedAttributeNode("school"))
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = {"id"})
+public class Student {
+
+ @Id
+ @Column(name = "student_id")
+ private String id;
+
+ @Column(name = "first_name")
+ private String firstName;
+
+ @Column(name = "last_name")
+ private String lastName;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "school_id", referencedColumnName = "school_id")
+ private School school;
+
+}
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentCustomQueryRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentCustomQueryRepository.java
new file mode 100644
index 0000000000..591c4a9157
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentCustomQueryRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.paging;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+public interface StudentCustomQueryRepository extends JpaRepository {
+
+ @Query(value = "SELECT stu FROM Student stu LEFT JOIN FETCH stu.school ",
+ countQuery = "SELECT COUNT(stu) FROM Student stu")
+ Page findAll(Pageable pageable);
+
+}
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentDTO.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentDTO.java
new file mode 100644
index 0000000000..b4fc068e68
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentDTO.java
@@ -0,0 +1,22 @@
+package com.baeldung.paging;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = {"id"})
+public class StudentDTO {
+
+ private String id;
+
+ private String firstName;
+
+ private String lastName;
+
+}
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentEntityGraphRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentEntityGraphRepository.java
new file mode 100644
index 0000000000..b63dcbbb8f
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentEntityGraphRepository.java
@@ -0,0 +1,13 @@
+package com.baeldung.paging;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentEntityGraphRepository extends JpaRepository {
+
+ @EntityGraph(attributePaths = "school")
+ Page findAll(Pageable pageable);
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentNamedEntityGraphRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentNamedEntityGraphRepository.java
new file mode 100644
index 0000000000..c901da3c30
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentNamedEntityGraphRepository.java
@@ -0,0 +1,13 @@
+package com.baeldung.paging;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentNamedEntityGraphRepository extends JpaRepository {
+
+ @EntityGraph(value = "Student.school")
+ Page findAll(Pageable pageable);
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentRepository.java
new file mode 100644
index 0000000000..e1bb78123c
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.paging;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentWithSchoolNameDTO.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentWithSchoolNameDTO.java
new file mode 100644
index 0000000000..ef634f78ac
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/paging/StudentWithSchoolNameDTO.java
@@ -0,0 +1,24 @@
+package com.baeldung.paging;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = {"id"})
+public class StudentWithSchoolNameDTO {
+
+ private String id;
+
+ private String firstName;
+
+ private String lastName;
+
+ private String schoolName;
+
+}
diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/paging/StudentRepositoryIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/paging/StudentRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..dbc1d5edb9
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/paging/StudentRepositoryIntegrationTest.java
@@ -0,0 +1,103 @@
+package com.baeldung.paging;
+
+import com.baeldung.listvsset.util.TestConfig;
+import io.hypersistence.utils.jdbc.validator.SQLStatementCountValidator;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.modelmapper.ModelMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static io.hypersistence.utils.jdbc.validator.SQLStatementCountValidator.assertSelectCount;
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+@SpringBootTest(classes = {PagingApplication.class, TestConfig.class}, properties = {
+ "spring.jpa.show-sql=true",
+ "spring.jpa.properties.hibernate.format_sql=true",
+ "spring.jpa.generate-ddl=true",
+ "spring.jpa.defer-datasource-initialization=true",
+ "spring.sql.init.data-locations=classpath:school-student-data.sql"
+})
+@Transactional
+class StudentRepositoryIntegrationTest {
+
+ @Autowired
+ private StudentRepository studentRepository;
+
+ @Autowired
+ private StudentCustomQueryRepository studentCustomQueryRepository;
+
+ @Autowired
+ private StudentEntityGraphRepository studentEntityGraphRepository;
+
+ @Autowired
+ private StudentNamedEntityGraphRepository studentNamedEntityGraphRepository;
+
+ private static final ModelMapper modelMapper = new ModelMapper();
+
+ @BeforeEach
+ void setUp() {
+ SQLStatementCountValidator.reset();
+ }
+
+ @Test
+ public void whenGetStudentsWithPageRequestOfTwo_thenReturnTwoRows() {
+ int rows = 2;
+ Pageable pageable = PageRequest.of(0, rows);
+ Page studentPage = studentRepository.findAll(pageable);
+
+ // Then
+ List studentList = studentPage.getContent();
+ assertThat(studentList.size()).isEqualTo(rows);
+ }
+
+ @Test
+ public void whenGetStudentsWithUnpagedAndSort_thenReturnAllResultsSorted() {
+ Sort sort = Sort.by(Sort.Direction.ASC, "lastName");
+ Pageable pageable = PageRequest.of(0, Integer.MAX_VALUE).withSort(sort);
+ Page studentPage = studentRepository.findAll(pageable);
+
+ // Then
+ List studentList = studentPage.getContent();
+ assertThat(studentList.size()).isEqualTo(studentPage.getTotalElements());
+ assertThat(studentList).isSortedAccordingTo(Comparator.comparing(Student::getLastName));
+ }
+
+
+ @Test
+ public void whenGetStudentsWithSchool_thenMultipleSelectQueriesAreExecuted() {
+ Page studentPage = studentRepository.findAll(Pageable.unpaged());
+ studentPage.get().map(student -> modelMapper.map(student, StudentWithSchoolNameDTO.class)).toList();
+ assertSelectCount(studentPage.getContent().size() + 1);
+ }
+
+ @Test
+ public void whenGetStudentsByCustomQuery_thenOneSelectQueryIsExecuted() {
+ Page studentPage = studentCustomQueryRepository.findAll(Pageable.unpaged());
+ studentPage.get().map(student -> modelMapper.map(student, StudentWithSchoolNameDTO.class)).toList();
+ assertSelectCount(1);
+ }
+
+ @Test
+ public void whenGetStudentsByEntityGraph_thenOneSelectQueryIsExecuted() {
+ Page studentPage = studentEntityGraphRepository.findAll(Pageable.unpaged());
+ studentPage.get().map(student -> modelMapper.map(student, StudentWithSchoolNameDTO.class)).toList();
+ assertSelectCount(1);
+ }
+
+ @Test
+ public void whenGetStudentsByNamedEntityGraph_thenOneSelectQueryIsExecuted() {
+ Page studentPage = studentNamedEntityGraphRepository.findAll(Pageable.unpaged());
+ studentPage.get().map(student -> modelMapper.map(student, StudentWithSchoolNameDTO.class)).toList();
+ assertSelectCount(1);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/school-student-data.sql b/persistence-modules/spring-boot-persistence-4/src/test/resources/school-student-data.sql
new file mode 100644
index 0000000000..de9699d256
--- /dev/null
+++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/school-student-data.sql
@@ -0,0 +1,9 @@
+INSERT INTO school (name) VALUES ('Ada Lovelace CE High School');
+INSERT INTO school (name) VALUES ('Ealing Fields High School');
+INSERT INTO school (name) VALUES ('Northolt High School');
+INSERT INTO school (name) VALUES ('Villiers High School');
+
+INSERT INTO student(student_id, first_name, last_name, school_id) VALUES('23056746', 'James', 'Drover', 1);
+INSERT INTO student(student_id, first_name, last_name, school_id) VALUES('23056751', 'Rubin', 'Webber', 2);
+INSERT INTO student(student_id, first_name, last_name, school_id) VALUES('23063444', 'Sarah', 'Pelham', 3);
+INSERT INTO student(student_id, first_name, last_name, school_id) VALUES('23065783', 'Lucy', 'Watson', 4);
\ No newline at end of file