Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/1 #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/main/java/com/e_co/e_commerce/config/LoadDatabase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.e_co.e_commerce.config;

import com.e_co.e_commerce.product.Product;
import com.e_co.e_commerce.product.ProductService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Random;

@Configuration
class LoadDatabase {

private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class);

@Bean
CommandLineRunner initDatabase(ProductService productService) {

return args -> {
// 프로덕트 등록
for (int i = 0; i < 25; i++) {
Random random = new Random();
log.info("Preloading {}", productService.save(new Product("TestProduct" + i, random.nextInt(10) * 1000 + 10000, "", random.nextInt(10), 1)));
}
};
}
}
104 changes: 104 additions & 0 deletions src/main/java/com/e_co/e_commerce/product/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.e_co.e_commerce.product;

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

import java.util.Objects;

@Entity

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public class Product {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AllArgConstructor

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

private String name;
private int price;
private String description;
private int stock;
private int sellerId;

public Product() {

}

public Product(String name, int price, String description, int stock, int sellerId) {
this.name = name;
this.price = price;
this.description = description;
this.stock = stock;
this.sellerId = sellerId;
}

public Long getId() {
return id;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public int getStock() {
return stock;
}

public void setStock(int stock) {
this.stock = stock;
}

public int getSellerId() {
return sellerId;
}

public void setSellerId(int sellerId) {
this.sellerId = sellerId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product product)) return false;
return price == product.price && stock == product.stock && sellerId == product.sellerId && Objects.equals(id, product.id) && Objects.equals(name, product.name) && Objects.equals(description, product.description);
}

@Override
public int hashCode() {
return Objects.hash(id, name, price, description, stock, sellerId);
}

@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
", description='" + description + '\'' +
", stock=" + stock +
", sellerId=" + sellerId +
'}';
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/e_co/e_commerce/product/ProductController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.e_co.e_commerce.product;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public class ProductController {
private ProductService productService;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private final ProductService productService;


@Autowired
public void setProductService(ProductService productService) {
this.productService = productService;
}

@GetMapping("/product")
public ResponseEntity<Product> getProduct(@RequestParam("id") long id) {
Product product = productService.getProduct(id);
return new ResponseEntity<>(product, HttpStatus.OK);
}

@GetMapping("/products")
public ResponseEntity<Page<Product>> getProducts(@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "isOnlyExist", defaultValue = "true", required = false) boolean isOnlyExist,
@PageableDefault Pageable pageable
) {
Page<Product> products = productService.getProducts(name, description, isOnlyExist, pageable);

return new ResponseEntity<>(products, HttpStatus.OK);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  return new ResponseEntity<>(productService.getProducts(name, description, isOnlyExist, pageable), HttpStatus.OK);

}
}
11 changes: 11 additions & 0 deletions src/main/java/com/e_co/e_commerce/product/ProductRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.e_co.e_commerce.product;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

public interface ProductRepository extends CrudRepository<Product, Long> {
// 페이지네이션, ((이름 or 내용) and 품절여부) 조건이 기본적으로 적용된 쿼리
// 정렬도 사용 가능
Page<Product> findAllByNameOrDescriptionOrStockGreaterThanEqual(String name, String description, int stock, Pageable pageable);
}
17 changes: 17 additions & 0 deletions src/main/java/com/e_co/e_commerce/product/ProductService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.e_co.e_commerce.product;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface ProductService {
long getCount();

Product save(Product product);

Product getProduct(long id);

/**
* 페이지네이션, 필터, 정렬이 적용된 여러 상품 가져오기
*/
Page<Product> getProducts(String name, String description, boolean isOnlyExist, Pageable pageable);
}
36 changes: 36 additions & 0 deletions src/main/java/com/e_co/e_commerce/product/ProductServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.e_co.e_commerce.product;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class ProductServiceImpl implements ProductService {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implements ProductService 제거 해주세요.

private ProductRepository productRepository;

@Autowired
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}

@Override
public long getCount() {
return productRepository.count();
}

@Override
public Product save(Product product) {
return productRepository.save(product);
}

@Override
public Product getProduct(long id) {
return productRepository.findById(id).orElseThrow();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orElseThrow(() -> new Exception("해당하는 id 에 값이 없습니다."))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NoSuch~Exception

}

@Override
public Page<Product> getProducts(String name, String description, boolean isOnlyExist, Pageable pageable) {
return productRepository.findAllByNameOrDescriptionOrStockGreaterThanEqual(name, description, isOnlyExist ? 1 : 0, pageable);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

productRepository.findAllByNameOrDescriptionOrStockGreaterThanEqual(name, description, isOnlyExist, pageable);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.e_co.e_commerce.product;

import net.bytebuddy.utility.RandomString;

import static org.junit.jupiter.api.Assertions.*;

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 org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

@SpringBootTest
public class ProductRepositoryTest {
@Autowired
ProductRepository productRepository;
List<Product> products;

@BeforeEach
public void setUp() {
productRepository.deleteAll();

products = new ArrayList<>();
for (int i = 0; i < 25; i++) {
Random random = new Random();
products.add(new Product("TestProduct" + i, random.nextInt(10) * 1000 + 10000, RandomString.make(), random.nextInt(10), 1));
}
}

@Test
public void save() {
assertEquals(0, productRepository.count());

ArrayList<Product> savedProducts = new ArrayList<>();
for (Product product : products) {
savedProducts.add(productRepository.save(product));
}

assertEquals(products.size(), productRepository.count());

for (Product product : savedProducts) {
assertEquals(product, productRepository.findById(product.getId()).orElseThrow(() -> new RuntimeException()));
}
}


// 페이지네이션, 조건, 정렬이 적용된 쿼리 테스트
@Test
public void findAllByNameOrDescription() {
String filterName = "";
String filterDescription = "";
boolean filterOnlyExist = false;

// 테스트 데이터를 자바 코드를 통해 쿼리에 맞춰 조건과 정렬 적용
List<Product> findProducts = products.stream().filter(product -> {
return (product.getName().contains(filterName) || product.getDescription().contains(filterDescription)) && product.getStock() >= (filterOnlyExist ? 1 : 0);
}).sorted(Comparator.comparingLong((Product p) -> p.getPrice()).reversed().thenComparing(p -> p.getId())).toList();

int pageSize = 10;
int pageTotal = (int) (Math.ceil((double) findProducts.size() / pageSize));

for (int i = 0; i < pageTotal; i++) {
Pageable pageable = PageRequest.of(i, pageSize, Sort.by("price").descending());
Page<Product> pages = productRepository.findAllByNameOrDescriptionOrStockGreaterThanEqual(filterName, filterDescription, filterOnlyExist ? 1 : 0, pageable);

assertEquals(pageTotal, pages.getTotalPages());
assertEquals(pageSize, pages.getSize());
assertEquals(findProducts.size(), pages.getTotalElements());

// 현재 쿼리가 ID 순서를 보장하지 못해 쿼리로 나온 페이지와 자바 코드로 나온 페이지 요소가 달라
// 요소별 테스트가 힘듬
// 이미 검증된 Spring Data JPA Repository 테스트에 가까워서 급하진 않기때문에
// 우선 요소 테스트 코드 작성은 패스

//List<Product> pagedProducts = findProducts.subList(i * pageSize, (i < pageTotal - 1) ? (i + 1) * pageSize : findProducts.size());
//// DB Query가 Id 순서를 보장하지 않아 자바 코드로 임시로 정렬
//List<Product> realPagedProducts = pages.getContent().stream().sorted(Comparator.comparingLong((Product p) -> p.getPrice()).reversed().thenComparing(p -> p.getId())).toList();
//for (int j = 0; j < pagedProducts.size(); j++) {
// assertEquals(pagedProducts.get(j), realPagedProducts.get(j));
//}
}
}
}
Loading
Loading