상품에 OneToMany 로직 추가

This commit is contained in:
kimscott
2019-11-21 16:51:59 +09:00
parent cc07dd551d
commit fb671dce11
11 changed files with 182 additions and 136 deletions

View File

@@ -1,30 +1,7 @@
# products
```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;
상품 추가시 상품 옵션은 OneToMany <-> ManyToOne 관계
@Configuration
public class RestTemplateConfig {
http POST http://localhost:8085/products name=TENT price=3000 stock=10
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper());
restTemplate.getMessageConverters().add(converter);
return restTemplate;
}
}
```
상품 추가
http POST http://localhost:8085/product < productData.json

15
productData.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "BOTTLE",
"price": 100,
"stock": 200,
"productOptions": [
{
"optionName": "color",
"desc": "red"
},
{
"optionName": "size" ,
"desc": "small"
}
]
}

View File

@@ -8,8 +8,6 @@ import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeTypeUtils;
import javax.persistence.Entity;
public class AbstractEvent {
String eventType;
@@ -31,6 +29,10 @@ public class AbstractEvent {
this.timestamp = timestamp;
}
public boolean isMe(){
return getEventType().equals(getClass().getSimpleName());
}
public String toJson(){
ObjectMapper objectMapper = new ObjectMapper();
String json = null;
@@ -44,4 +46,21 @@ public class AbstractEvent {
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());
}
}
}

View File

@@ -7,6 +7,9 @@ import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
@EnableBinding(KafkaProcessor.class)
public class Application {
@@ -27,6 +30,21 @@ public class Application {
product.setPrice(i*10000);
product.setStock(i*10);
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++;
productRepository.save(product);
}

View File

@@ -1,14 +1,20 @@
package com.example.template;
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.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.MimeTypeUtils;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class Product {
@Id
@@ -20,26 +26,13 @@ public class Product {
int stock;
String imageUrl;
@OneToMany(fetch=FetchType.LAZY, mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval=true)
private List<ProductOption> productOptions = new ArrayList<ProductOption>();
@PostPersist @PostUpdate
private void publishStart() {
ProductChanged productChanged = new ProductChanged(this);
String json = 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());
}
productChanged.sendMessage(productChanged.toJson());
}
public Long getId() {
@@ -81,4 +74,17 @@ public class Product {
public void setImageUrl(String 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);
}
}

View File

@@ -18,23 +18,7 @@ public class ProductChanged extends AbstractEvent{
private Long productId;
private String productName;
private int productPrice;
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;
public ProductChanged(){
@@ -49,7 +33,6 @@ public class ProductChanged extends AbstractEvent{
this.setProductName(product.getName());
this.setProductPrice(product.getPrice());
this.setProductStock(product.getStock());
// this.setStock(product.getStock());
this.setImageUrl(product.getImageUrl());
}
@@ -91,5 +74,11 @@ public class ProductChanged extends AbstractEvent{
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public int getProductStock() {
return productStock;
}
public void setProductStock(int productStock) {
this.productStock = productStock;
}
}

View File

@@ -1,9 +1,7 @@
package com.example.template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
@RestController
public class ProductController {
@@ -11,13 +9,14 @@ public class ProductController {
@Autowired
ProductService productService;
// @GetMapping("/item/{productId}")
// Product productStockCheck(@PathVariable(value = "productId") Long productId) {
// return this.productService.getProductById(productId);
// }
@GetMapping("/product/{productId}")
Product productStockCheck1(@PathVariable(value = "productId") Long productId) {
Product productStockCheck(@PathVariable(value = "productId") Long productId) {
return this.productService.getProductById(productId);
}
@PostMapping("/product")
Product productInsert(@RequestBody String data) {
System.out.println(data);
return this.productService.save(data);
}
}

View 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;
}
}

View File

@@ -0,0 +1,6 @@
package com.example.template;
import org.springframework.data.repository.CrudRepository;
public interface ProductOptionRepository extends CrudRepository<ProductOption, Long> {
}

View File

@@ -8,6 +8,8 @@ import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
@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());
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;
}
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);
}
}

View File

@@ -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);
}
}
}
}