상품에 OneToMany 로직 추가
This commit is contained in:
29
README.md
29
README.md
@@ -1,30 +1,7 @@
|
|||||||
# products
|
# products
|
||||||
|
상품 추가시 상품 옵션은 OneToMany <-> ManyToOne 관계
|
||||||
```java
|
|
||||||
package com.example.template.config.rest;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
http POST http://localhost:8085/products name=TENT price=3000 stock=10
|
||||||
public class RestTemplateConfig {
|
|
||||||
|
|
||||||
@Bean
|
http POST http://localhost:8085/product < productData.json
|
||||||
RestTemplate restTemplate() {
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
|
||||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
|
||||||
converter.setObjectMapper(new ObjectMapper());
|
|
||||||
restTemplate.getMessageConverters().add(converter);
|
|
||||||
return restTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
상품 추가
|
|
||||||
15
productData.json
Normal file
15
productData.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "BOTTLE",
|
||||||
|
"price": 100,
|
||||||
|
"stock": 200,
|
||||||
|
"productOptions": [
|
||||||
|
{
|
||||||
|
"optionName": "color",
|
||||||
|
"desc": "red"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"optionName": "size" ,
|
||||||
|
"desc": "small"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -8,8 +8,6 @@ import org.springframework.messaging.MessageHeaders;
|
|||||||
import org.springframework.messaging.support.MessageBuilder;
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
import org.springframework.util.MimeTypeUtils;
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
|
|
||||||
public class AbstractEvent {
|
public class AbstractEvent {
|
||||||
|
|
||||||
String eventType;
|
String eventType;
|
||||||
@@ -31,6 +29,10 @@ public class AbstractEvent {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMe(){
|
||||||
|
return getEventType().equals(getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
public String toJson(){
|
public String toJson(){
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
String json = null;
|
String json = null;
|
||||||
@@ -44,4 +46,21 @@ public class AbstractEvent {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendMessage(String json){
|
||||||
|
if( json != null ){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spring streams 방식
|
||||||
|
*/
|
||||||
|
KafkaProcessor processor = Application.applicationContext.getBean(KafkaProcessor.class);
|
||||||
|
MessageChannel outputChannel = processor.outboundTopic();
|
||||||
|
|
||||||
|
outputChannel.send(MessageBuilder
|
||||||
|
.withPayload(json)
|
||||||
|
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
|
|||||||
import org.springframework.cloud.stream.messaging.Processor;
|
import org.springframework.cloud.stream.messaging.Processor;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableBinding(KafkaProcessor.class)
|
@EnableBinding(KafkaProcessor.class)
|
||||||
public class Application {
|
public class Application {
|
||||||
@@ -27,6 +30,21 @@ public class Application {
|
|||||||
product.setPrice(i*10000);
|
product.setPrice(i*10000);
|
||||||
product.setStock(i*10);
|
product.setStock(i*10);
|
||||||
product.setImageUrl("/goods/img/"+p+".jpg");
|
product.setImageUrl("/goods/img/"+p+".jpg");
|
||||||
|
|
||||||
|
// 상품 디테일 추가 - 양방향 관계
|
||||||
|
ProductOption productOption = new ProductOption();
|
||||||
|
productOption.setName(p + "_detail");
|
||||||
|
productOption.setDesc(p + "_desc");
|
||||||
|
productOption.setProduct(product);
|
||||||
|
|
||||||
|
ProductOption productOption1 = new ProductOption();
|
||||||
|
productOption1.setName(p + "구매설명");
|
||||||
|
productOption1.setDesc(p + "설명입니다");
|
||||||
|
productOption1.setProduct(product);
|
||||||
|
|
||||||
|
product.addProductOptions(productOption);
|
||||||
|
product.addProductOptions(productOption1);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
productRepository.save(product);
|
productRepository.save(product);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package com.example.template;
|
package com.example.template;
|
||||||
|
|
||||||
import com.example.template.config.kafka.KafkaProcessor;
|
import com.example.template.config.kafka.KafkaProcessor;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
||||||
import org.springframework.messaging.MessageChannel;
|
import org.springframework.messaging.MessageChannel;
|
||||||
import org.springframework.messaging.MessageHeaders;
|
import org.springframework.messaging.MessageHeaders;
|
||||||
import org.springframework.messaging.support.MessageBuilder;
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
import org.springframework.util.MimeTypeUtils;
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
|
||||||
public class Product {
|
public class Product {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@@ -20,26 +26,13 @@ public class Product {
|
|||||||
int stock;
|
int stock;
|
||||||
String imageUrl;
|
String imageUrl;
|
||||||
|
|
||||||
|
@OneToMany(fetch=FetchType.LAZY, mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval=true)
|
||||||
|
private List<ProductOption> productOptions = new ArrayList<ProductOption>();
|
||||||
|
|
||||||
@PostPersist @PostUpdate
|
@PostPersist @PostUpdate
|
||||||
private void publishStart() {
|
private void publishStart() {
|
||||||
|
|
||||||
ProductChanged productChanged = new ProductChanged(this);
|
ProductChanged productChanged = new ProductChanged(this);
|
||||||
String json = productChanged.toJson();
|
productChanged.sendMessage(productChanged.toJson());
|
||||||
|
|
||||||
if( json != null ){
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spring streams 방식
|
|
||||||
*/
|
|
||||||
KafkaProcessor processor = Application.applicationContext.getBean(KafkaProcessor.class);
|
|
||||||
MessageChannel outputChannel = processor.outboundTopic();
|
|
||||||
|
|
||||||
outputChannel.send(MessageBuilder
|
|
||||||
.withPayload(json)
|
|
||||||
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
@@ -81,4 +74,17 @@ public class Product {
|
|||||||
public void setImageUrl(String imageUrl) {
|
public void setImageUrl(String imageUrl) {
|
||||||
this.imageUrl = imageUrl;
|
this.imageUrl = imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ProductOption> getProductOptions() {
|
||||||
|
return productOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductOptions(List<ProductOption> productOptions) {
|
||||||
|
this.productOptions = productOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProductOptions(ProductOption productOption){
|
||||||
|
productOptions.add(productOption);
|
||||||
|
productOption.setProduct(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,7 @@ public class ProductChanged extends AbstractEvent{
|
|||||||
private Long productId;
|
private Long productId;
|
||||||
private String productName;
|
private String productName;
|
||||||
private int productPrice;
|
private int productPrice;
|
||||||
|
|
||||||
private int productStock;
|
private int productStock;
|
||||||
public int getProductStock() {
|
|
||||||
return productStock;
|
|
||||||
}
|
|
||||||
public void setProductStock(int productStock) {
|
|
||||||
this.productStock = productStock;
|
|
||||||
}
|
|
||||||
|
|
||||||
// private int stock;
|
|
||||||
// public int getStock() {
|
|
||||||
// return stock;
|
|
||||||
// }
|
|
||||||
// public void setStock(int stock) {
|
|
||||||
// this.stock = stock;
|
|
||||||
// }
|
|
||||||
|
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
|
|
||||||
public ProductChanged(){
|
public ProductChanged(){
|
||||||
@@ -49,7 +33,6 @@ public class ProductChanged extends AbstractEvent{
|
|||||||
this.setProductName(product.getName());
|
this.setProductName(product.getName());
|
||||||
this.setProductPrice(product.getPrice());
|
this.setProductPrice(product.getPrice());
|
||||||
this.setProductStock(product.getStock());
|
this.setProductStock(product.getStock());
|
||||||
// this.setStock(product.getStock());
|
|
||||||
this.setImageUrl(product.getImageUrl());
|
this.setImageUrl(product.getImageUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,5 +74,11 @@ public class ProductChanged extends AbstractEvent{
|
|||||||
public void setImageUrl(String imageUrl) {
|
public void setImageUrl(String imageUrl) {
|
||||||
this.imageUrl = imageUrl;
|
this.imageUrl = imageUrl;
|
||||||
}
|
}
|
||||||
|
public int getProductStock() {
|
||||||
|
return productStock;
|
||||||
|
}
|
||||||
|
public void setProductStock(int productStock) {
|
||||||
|
this.productStock = productStock;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.example.template;
|
package com.example.template;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class ProductController {
|
public class ProductController {
|
||||||
@@ -11,13 +9,14 @@ public class ProductController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
ProductService productService;
|
ProductService productService;
|
||||||
|
|
||||||
// @GetMapping("/item/{productId}")
|
|
||||||
// Product productStockCheck(@PathVariable(value = "productId") Long productId) {
|
|
||||||
// return this.productService.getProductById(productId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@GetMapping("/product/{productId}")
|
@GetMapping("/product/{productId}")
|
||||||
Product productStockCheck1(@PathVariable(value = "productId") Long productId) {
|
Product productStockCheck(@PathVariable(value = "productId") Long productId) {
|
||||||
return this.productService.getProductById(productId);
|
return this.productService.getProductById(productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/product")
|
||||||
|
Product productInsert(@RequestBody String data) {
|
||||||
|
System.out.println(data);
|
||||||
|
return this.productService.save(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/main/java/com/example/template/ProductOption.java
Normal file
62
src/main/java/com/example/template/ProductOption.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package com.example.template;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
|
||||||
|
public class ProductOption {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy=GenerationType.IDENTITY)
|
||||||
|
Long id;
|
||||||
|
String name;
|
||||||
|
String optionName;
|
||||||
|
String desc;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name="PRODUCT_ID", referencedColumnName="id" , nullable = true )
|
||||||
|
private Product product;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOptionName() {
|
||||||
|
return optionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOptionName(String optionName) {
|
||||||
|
this.optionName = optionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDesc(String desc) {
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product getProduct() {
|
||||||
|
return product;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProduct(Product product) {
|
||||||
|
this.product = product;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.example.template;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
public interface ProductOptionRepository extends CrudRepository<ProductOption, Long> {
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ import org.springframework.cloud.stream.annotation.StreamListener;
|
|||||||
import org.springframework.messaging.handler.annotation.Payload;
|
import org.springframework.messaging.handler.annotation.Payload;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -30,7 +32,7 @@ public class ProductService {
|
|||||||
/**
|
/**
|
||||||
* 주문이 발생시, 수량을 줄인다.
|
* 주문이 발생시, 수량을 줄인다.
|
||||||
*/
|
*/
|
||||||
if( orderPlaced.getEventType().equals(OrderPlaced.class.getSimpleName())){
|
if( orderPlaced.isMe()){
|
||||||
|
|
||||||
Optional<Product> productOptional = productRepository.findById(orderPlaced.getProductId());
|
Optional<Product> productOptional = productRepository.findById(orderPlaced.getProductId());
|
||||||
Product product = productOptional.get();
|
Product product = productOptional.get();
|
||||||
@@ -45,35 +47,6 @@ public class ProductService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @KafkaListener(topics = "${eventTopic}")
|
|
||||||
// public void onOrderPlaced(@Payload String message, ConsumerRecord<?, ?> consumerRecord) {
|
|
||||||
// System.out.println("##### listener : " + message);
|
|
||||||
//
|
|
||||||
// ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
// objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
//
|
|
||||||
// OrderPlaced orderPlaced = null;
|
|
||||||
// try {
|
|
||||||
// orderPlaced = objectMapper.readValue(message, OrderPlaced.class);
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 주문이 발생시, 수량을 줄인다.
|
|
||||||
// */
|
|
||||||
// if( orderPlaced.getEventType().equals(OrderPlaced.class.getSimpleName())){
|
|
||||||
//
|
|
||||||
// Optional<Product> productOptional = productRepository.findById(orderPlaced.getProductId());
|
|
||||||
// Product product = productOptional.get();
|
|
||||||
// product.setStock(product.getStock() - orderPlaced.getQuantity());
|
|
||||||
//
|
|
||||||
// productRepository.save(product);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }catch (Exception e){
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 상품 조회
|
* 상품 조회
|
||||||
*/
|
*/
|
||||||
@@ -84,4 +57,21 @@ public class ProductService {
|
|||||||
|
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Product save(String data){
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
Product product = null;
|
||||||
|
try {
|
||||||
|
product = mapper.readValue(data, Product.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProductOption> productOptions = product.getProductOptions();
|
||||||
|
for(ProductOption p : productOptions){
|
||||||
|
p.setProduct(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
return productRepository.save(product);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.example.template.config;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
|
||||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
|
||||||
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class RepositoryRestConfig implements RepositoryRestConfigurer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
|
|
||||||
|
|
||||||
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
|
|
||||||
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
|
|
||||||
|
|
||||||
final Set<BeanDefinition> beans = provider.findCandidateComponents("com.example");
|
|
||||||
|
|
||||||
for (BeanDefinition bean : beans) {
|
|
||||||
Class<?> idExposedClasses = null;
|
|
||||||
try {
|
|
||||||
idExposedClasses = Class.forName(bean.getBeanClassName());
|
|
||||||
config.exposeIdsFor(Class.forName(idExposedClasses.getName()));
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// Can't throw ClassNotFoundException due to the method signature. Need to cast it
|
|
||||||
throw new RuntimeException("Failed to expose `id` field due to", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user