From aae5f7dc38d0f7edc97bef98c61a92abb18364b4 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Jul 2023 06:00:54 -0400 Subject: [PATCH] Scroll API Implementation (#14435) Co-authored-by: technoddy --- .../spring-boot-persistence-4/pom.xml | 63 ++++++++++++++++ .../scrollapi/ScrollAPIApplication.java | 11 +++ .../baeldung/scrollapi/entity/BookReview.java | 53 ++++++++++++++ .../scrollapi/repository/BookRepository.java | 16 ++++ .../baeldung/scrollapi/service/BookLogic.java | 53 ++++++++++++++ .../src/main/resources/application.yml | 4 + .../scrollapi/service/BookLogicUnitTest.java | 73 +++++++++++++++++++ .../src/test/resources/application.properties | 5 ++ 8 files changed, 278 insertions(+) create mode 100644 persistence-modules/spring-boot-persistence-4/pom.xml create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/ScrollAPIApplication.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/entity/BookReview.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/repository/BookRepository.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/service/BookLogic.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/resources/application.yml create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/boot/scrollapi/service/BookLogicUnitTest.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/resources/application.properties diff --git a/persistence-modules/spring-boot-persistence-4/pom.xml b/persistence-modules/spring-boot-persistence-4/pom.xml new file mode 100644 index 0000000000..99c39e205d --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + com.baeldung.boot.persistence + spring-boot-persistence-4 + 0.0.1-SNAPSHOT + spring-boot-persistence-4 + + + + + org.junit + junit-bom + ${junit-jupiter.version} + pom + import + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.dependencies} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + 3.1.0 + 5.9.3 + 17 + 17 + + + \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/ScrollAPIApplication.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/ScrollAPIApplication.java new file mode 100644 index 0000000000..27e6555e26 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/ScrollAPIApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.scrollapi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ScrollAPIApplication { + public static void main(String[] args) { + SpringApplication.run(ScrollAPIApplication.class, args); + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/entity/BookReview.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/entity/BookReview.java new file mode 100644 index 0000000000..4baa2cda83 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/entity/BookReview.java @@ -0,0 +1,53 @@ +package com.baeldung.scrollapi.entity; + +import jakarta.persistence.*; + +@Entity +@Table(name = "BOOK_REVIEWS") +public class BookReview { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq") + @SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1) + private Long reviewsId; + private String userId; + private String isbn; + private String bookRating; + + public Long getReviewsId() { + return reviewsId; + } + + public void setReviewsId(Long reviewsId) { + this.reviewsId = reviewsId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getIsbn() { + return isbn; + } + + public void setIsbn(String isbn) { + this.isbn = isbn; + } + + public String getBookRating() { + return bookRating; + } + + public void setBookRating(String bookRating) { + this.bookRating = bookRating; + } + + @Override + public String toString() { + return "BookReview{" + "reviewsId=" + reviewsId + ", userId='" + userId + '\'' + ", isbn='" + isbn + '\'' + ", bookRating='" + bookRating + '\'' + '}'; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/repository/BookRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/repository/BookRepository.java new file mode 100644 index 0000000000..717cbc31c7 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/repository/BookRepository.java @@ -0,0 +1,16 @@ +package com.baeldung.scrollapi.repository; + +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.OffsetScrollPosition; +import org.springframework.data.domain.Window; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.Repository; + +import com.baeldung.scrollapi.entity.BookReview; + +public interface BookRepository extends JpaRepository { + + Window findFirst5ByBookRating(String bookRating, OffsetScrollPosition position); + + Window findFirst5ByBookRating(String bookRating, KeysetScrollPosition position); +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/service/BookLogic.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/service/BookLogic.java new file mode 100644 index 0000000000..aacf0e3a78 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/scrollapi/service/BookLogic.java @@ -0,0 +1,53 @@ +package com.baeldung.scrollapi.service; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.KeysetScrollPosition; +import org.springframework.data.domain.OffsetScrollPosition; +import org.springframework.data.domain.ScrollPosition; +import org.springframework.data.domain.Window; +import org.springframework.data.support.WindowIterator; +import org.springframework.stereotype.Service; + +import com.baeldung.scrollapi.entity.BookReview; +import com.baeldung.scrollapi.repository.BookRepository; + +@Service +public class BookLogic { + + @Autowired + private BookRepository bookRepository; + + public List getBooksUsingOffset(String rating) { + OffsetScrollPosition offset = ScrollPosition.offset(); + + Window bookReviews = bookRepository.findFirst5ByBookRating(rating, offset); + List bookReviewsResult = new ArrayList<>(); + do { + bookReviews.forEach(bookReviewsResult::add); + bookReviews = bookRepository.findFirst5ByBookRating(rating, (OffsetScrollPosition) bookReviews.positionAt(bookReviews.size() - 1)); + } while (!bookReviews.isEmpty() && bookReviews.hasNext()); + + return bookReviewsResult; + } + + public List getBooksUsingOffSetFilteringAndWindowIterator(String rating) { + WindowIterator bookReviews = WindowIterator.of(position -> bookRepository.findFirst5ByBookRating("3.5", (OffsetScrollPosition) position)) + .startingAt(ScrollPosition.offset()); + List bookReviewsResult = new ArrayList<>(); + + bookReviews.forEachRemaining(bookReviewsResult::add); + return bookReviewsResult; + } + + public List getBooksUsingKeySetFiltering(String rating) { + WindowIterator bookReviews = WindowIterator.of(position -> bookRepository.findFirst5ByBookRating(rating, (KeysetScrollPosition) position)) + .startingAt(ScrollPosition.keyset()); + List bookReviewsResult = new ArrayList<>(); + + bookReviews.forEachRemaining(bookReviewsResult::add); + return bookReviewsResult; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/resources/application.yml b/persistence-modules/spring-boot-persistence-4/src/main/resources/application.yml new file mode 100644 index 0000000000..bb9e377c34 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/resources/application.yml @@ -0,0 +1,4 @@ + +logging.level.org.hibernate: + SQL: DEBUG + type.descriptor.sql.BasicBinder: TRACE \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/boot/scrollapi/service/BookLogicUnitTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/boot/scrollapi/service/BookLogicUnitTest.java new file mode 100644 index 0000000000..be816e62cf --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/boot/scrollapi/service/BookLogicUnitTest.java @@ -0,0 +1,73 @@ +package com.baeldung.boot.scrollapi.service; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.List; +import java.util.UUID; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import com.baeldung.scrollapi.ScrollAPIApplication; +import com.baeldung.scrollapi.entity.BookReview; +import com.baeldung.scrollapi.repository.BookRepository; +import com.baeldung.scrollapi.service.BookLogic; + +@SpringBootTest(classes = ScrollAPIApplication.class) +class BookLogicUnitTest { + + @Autowired + private BookRepository bookRepository; + + @Autowired + private BookLogic bookLogic; + + @BeforeEach + public void beforeEach() { + IntStream.rangeClosed(1, 5) + .forEach(i -> insertBookReview()); + } + + @AfterEach + public void afterEach() { + bookRepository.deleteAll(); + } + + @Test + public void givenBookReviewInTable_whenGetBooksUsingOffset_returnsBookReviews() { + List bookReviews = bookLogic.getBooksUsingOffset("3.5"); + assertThat(bookReviews.size()).isEqualTo(5); + } + + @Test + public void givenBookReviewInTable_whenGetBooksUsingOffSetFilteringAndWindowIterator_returnsBookReviews() { + List bookReviews = bookLogic.getBooksUsingOffSetFilteringAndWindowIterator("3.5"); + assertThat(bookReviews.size()).isEqualTo(5); + } + + @Test + public void givenBookReviewInTable_whenGetBooksUsingKeySetFiltering_returnsBookReviews() { + List bookReviews = bookLogic.getBooksUsingKeySetFiltering("3.5"); + assertThat(bookReviews.size()).isEqualTo(5); + } + + private void insertBookReview() { + BookReview bookReview = getBookReview(); + bookRepository.save(bookReview); + } + + private static BookReview getBookReview() { + BookReview bookReview = new BookReview(); + String seed = UUID.randomUUID() + .toString(); + bookReview.setIsbn("isbn" + seed); + bookReview.setBookRating("3.5"); + bookReview.setUserId(seed); + + return bookReview; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/application.properties b/persistence-modules/spring-boot-persistence-4/src/test/resources/application.properties new file mode 100644 index 0000000000..a21dcc731e --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/application.properties @@ -0,0 +1,5 @@ +# spring.datasource.x +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa \ No newline at end of file