상품에 OneToMany 로직 추가
This commit is contained in:
29
README.md
29
README.md
@@ -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
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.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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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