From 6d1ed25da1642aab6aaaf5d9bbc16590cfe22e35 Mon Sep 17 00:00:00 2001 From: Manfred <77407079+manfred106@users.noreply.github.com> Date: Wed, 28 Feb 2024 02:13:46 +0000 Subject: [PATCH] BAEL-7572: Spring Data JPA Repository for Database View (#15918) --- .../dbview/DatabaseViewApplication.java | 13 ++++ .../java/com/baeldung/dbview/ShopSale.java | 43 ++++++++++++ .../baeldung/dbview/ShopSaleCompositeId.java | 24 +++++++ .../baeldung/dbview/ShopSaleRepository.java | 9 +++ .../java/com/baeldung/dbview/ShopSaleVid.java | 46 +++++++++++++ .../dbview/ShopSaleVidRepository.java | 9 +++ .../baeldung/dbview/ViewNoIdRepository.java | 15 +++++ .../com/baeldung/dbview/ViewRepository.java | 22 +++++++ .../ShopSaleRepositoryIntegrationTest.java | 65 +++++++++++++++++++ .../ShopSaleVidRepositoryIntegrationTest.java | 41 ++++++++++++ .../src/test/resources/shop-sale-data.sql | 47 ++++++++++++++ 11 files changed, 334 insertions(+) create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/DatabaseViewApplication.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSale.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleCompositeId.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleRepository.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVid.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVidRepository.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewNoIdRepository.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewRepository.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleRepositoryIntegrationTest.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleVidRepositoryIntegrationTest.java create mode 100644 persistence-modules/spring-boot-persistence-4/src/test/resources/shop-sale-data.sql diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/DatabaseViewApplication.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/DatabaseViewApplication.java new file mode 100644 index 0000000000..be8e8caaaa --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/DatabaseViewApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.dbview; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DatabaseViewApplication { + + public static void main(String[] args) { + SpringApplication.run(DatabaseViewApplication.class, args); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSale.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSale.java new file mode 100644 index 0000000000..6e7b950bc0 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSale.java @@ -0,0 +1,43 @@ +package com.baeldung.dbview; + +import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +@Entity +@Table(name = "SHOP_SALE_VIEW") +@Getter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +public class ShopSale { + + @EmbeddedId + @AttributeOverrides({ + @AttributeOverride( name = "shopId", column = @Column(name = "shop_id")), + @AttributeOverride( name = "year", column = @Column(name = "transaction_year")), + @AttributeOverride( name = "month", column = @Column(name = "transaction_month")) + }) + private ShopSaleCompositeId id; + + @Column(name = "shop_location", length = 100) + private String shopLocation; + + @Column(name = "total_amount") + private BigDecimal totalAmount; + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleCompositeId.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleCompositeId.java new file mode 100644 index 0000000000..721f4556c7 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleCompositeId.java @@ -0,0 +1,24 @@ +package com.baeldung.dbview; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class ShopSaleCompositeId { + + private int shopId; + + private int year; + + private int month; + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleRepository.java new file mode 100644 index 0000000000..bf765a75a8 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.dbview; + +import java.util.List; + +public interface ShopSaleRepository extends ViewRepository { + + List findByIdShopId(Integer shopId); + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVid.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVid.java new file mode 100644 index 0000000000..a5f63b1bbb --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVid.java @@ -0,0 +1,46 @@ +package com.baeldung.dbview; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.math.BigDecimal; + +@Entity +@Table(name = "SHOP_SALE_VIEW") +@Getter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +public class ShopSaleVid { + + @Id + @Column(name = "id") + private Long id; + + @Column(name = "shop_id") + private int shopId; + + @Column(name = "shop_location", length = 100) + private String shopLocation; + + @Column(name = "transaction_year") + private int year; + + @Column(name = "transaction_month") + private int month; + + @Column(name = "total_amount") + private BigDecimal totalAmount; + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVidRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVidRepository.java new file mode 100644 index 0000000000..c8cdfefaa5 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ShopSaleVidRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.dbview; + +import java.util.List; + +public interface ShopSaleVidRepository extends ViewNoIdRepository { + + List findByShopId(Integer shopId); + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewNoIdRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewNoIdRepository.java new file mode 100644 index 0000000000..4923771fed --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewNoIdRepository.java @@ -0,0 +1,15 @@ +package com.baeldung.dbview; + +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.Repository; + +import java.util.List; + +@NoRepositoryBean +public interface ViewNoIdRepository extends Repository { + + long count(); + + List findAll(); + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewRepository.java new file mode 100644 index 0000000000..c6d56987ec --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/dbview/ViewRepository.java @@ -0,0 +1,22 @@ +package com.baeldung.dbview; + +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.Repository; + +import java.util.List; +import java.util.Optional; + +@NoRepositoryBean +public interface ViewRepository extends Repository { + + long count(); + + boolean existsById(K id); + + List findAll(); + + List findAllById(Iterable ids); + + Optional findById(K id); + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleRepositoryIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleRepositoryIntegrationTest.java new file mode 100644 index 0000000000..ed7a07dcb8 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleRepositoryIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung.dbview; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = DatabaseViewApplication.class, properties = { + "spring.jpa.show-sql=true", + "spring.jpa.properties.hibernate.format_sql=true", + "spring.jpa.hibernate.ddl-auto=none", + "spring.jpa.defer-datasource-initialization=true", + "spring.sql.init.data-locations=classpath:shop-sale-data.sql" +}) +class ShopSaleRepositoryIntegrationTest { + + private static final ShopSaleCompositeId id = ShopSaleCompositeId.builder() + .shopId(1) + .year(2024) + .month(1) + .build(); + + @Autowired + private ShopSaleRepository shopSaleRepository; + + @Test + void whenCount_thenValueGreaterThanOne() { + assertThat(shopSaleRepository.count()).isGreaterThan(0); + } + + @Test + void whenFindAll_thenReturnAllRecords() { + assertThat(shopSaleRepository.findAll()).isNotEmpty(); + } + + @Test + void whenExistsById_thenReturnTrue() { + assertThat(shopSaleRepository.existsById(id)).isTrue(); + } + + @Test + void whenFindAllById_thenReturnListWithTwoInstances() { + assertThat(shopSaleRepository.findAllById(List.of (id))).hasSize(1); + } + + @Test + void whenFindById_thenReturnAnInstance() { + assertThat(shopSaleRepository.findById(id).isPresent()).isTrue(); + } + + @Test + void whenFindByShopId_thenReturnAllShopSaleOfThatShop() { + var shopId = 1; + List shopSaleVidList = shopSaleRepository.findByIdShopId(shopId); + assertThat(shopSaleVidList).isNotEmpty(); + shopSaleVidList.forEach(s -> assertThat(s.getId().getShopId()).isEqualTo(shopId)); + } + + + + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleVidRepositoryIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleVidRepositoryIntegrationTest.java new file mode 100644 index 0000000000..4b0b44a1a7 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/dbview/ShopSaleVidRepositoryIntegrationTest.java @@ -0,0 +1,41 @@ +package com.baeldung.dbview; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = DatabaseViewApplication.class, properties = { + "spring.jpa.show-sql=true", + "spring.jpa.properties.hibernate.format_sql=true", + "spring.jpa.hibernate.ddl-auto=none", + "spring.jpa.defer-datasource-initialization=true", + "spring.sql.init.data-locations=classpath:shop-sale-data.sql" +}) +class ShopSaleVidRepositoryIntegrationTest { + + @Autowired + private ShopSaleVidRepository shopSaleVidRepository; + + @Test + void whenCount_thenValueGreaterThanOne() { + assertThat(shopSaleVidRepository.count()).isGreaterThan(0); + } + + @Test + void whenFindAll_thenReturnAllRecords() { + assertThat(shopSaleVidRepository.findAll()).isNotEmpty(); + } + + @Test + void whenFindByShopId_thenReturnAllShopSaleOfThatShop() { + var shopId = 1; + List shopSaleList = shopSaleVidRepository.findByShopId(shopId); + assertThat(shopSaleList).isNotEmpty(); + shopSaleList.forEach(s -> assertThat(s.getShopId()).isEqualTo(shopId)); + } + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/shop-sale-data.sql b/persistence-modules/spring-boot-persistence-4/src/test/resources/shop-sale-data.sql new file mode 100644 index 0000000000..7d16764818 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/shop-sale-data.sql @@ -0,0 +1,47 @@ +CREATE TABLE SHOP +( + shop_id int AUTO_INCREMENT, + shop_location varchar(100) NOT NULL UNIQUE, + PRIMARY KEY(shop_id) +); + + +CREATE TABLE SHOP_TRANSACTION +( + transaction_id bigint AUTO_INCREMENT, + transaction_date date NOT NULL, + shop_id int NOT NULL, + amount decimal(8,2) NOT NULL, + PRIMARY KEY(transaction_id), + FOREIGN KEY(shop_id) REFERENCES SHOP(shop_id) +); + + +CREATE VIEW SHOP_SALE_VIEW AS +SELECT ROW_NUMBER() OVER () AS id, shop_id, shop_location, transaction_year, transaction_month, SUM(amount) AS total_amount +FROM ( + SELECT shop.shop_id, shop.shop_location, trans.amount, YEAR(transaction_date) AS transaction_year, MONTH(transaction_date) AS transaction_month + FROM SHOP shop, SHOP_TRANSACTION trans + WHERE shop.shop_id = trans.shop_id +) SHOP_MONTH_TRANSACTION +GROUP BY shop_id, transaction_year, transaction_month; + + +INSERT INTO SHOP(shop_location) VALUES ('Ealing'); +INSERT INTO SHOP(shop_location) VALUES ('Richmond'); + +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-01-05', 1, 3.49); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-01-31', 1, 7.29); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-02-09', 1, 1.60); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-02-17', 1, 5.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-02-18', 1, 5.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-03-01', 1, 8.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-03-22', 1, 5.49); + +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-01-15', 2, 8.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-01-18', 2, 8.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-02-01', 2, 5.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-02-05', 2, 2.50); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-03-01', 2, 5.99); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-03-02', 2, 6.60); +INSERT INTO SHOP_TRANSACTION(transaction_date, shop_id, amount) VALUES ('2024-03-17', 2, 1.19);