-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Feature/1 #2
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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))); | ||
} | ||
}; | ||
} | ||
} |
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 | ||
public class Product { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 + | ||
'}'; | ||
} | ||
} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
public class ProductController { | ||
private ProductService productService; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
@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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} |
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); | ||
} |
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); | ||
} |
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. orElseThrow(() -> new Exception("해당하는 id 에 값이 없습니다.")) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
//} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Getter