REST API를 사용하여 CART(장바구니) 서비스 구현
DB 관계도
- 하나의 product가 여러개의 cart item에 담겨 여러개의 cart에 담길 수 있다.
- List<CartItem>은 DB에 따로 저장하지 않는다.
Cart와 CartItem 생성
-Cart.java
package kr.ac.hansung.cse.model;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Cart {
private int id;
private List<CartItem> cartItems = new ArrayList<CartItem>();
private double grandTotal;
}
-CartItem.java
package kr.ac.hansung.cse.model;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class CartItem {
private int id;
private Cart cart;
private Product product;
private int quantity;
private double totalPrice;
}
java serialization
- Serialization : 객체를 File, DB, Memory, Network 에 보낼 때에는 Byte Stream으로 바꾸어서 보낸다.
- De-Serialization : Byte Stream을 다시 객체화 하는 과정을 뜻한다.
Object -> JSON으로 Serialization시키고, JSON -> Object로 De-Serialization 할 계획
-> 이 과정을 하기 위해 Serializable 인터페이스를 implements 해준다.
-Cart.java
package kr.ac.hansung.cse.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
public class Cart implements Serializable {
private static final long serialVersionUID = -7383420736137539222L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private List<CartItem> cartItems = new ArrayList<CartItem>();
private double grandTotal;
}
-CartItem.java
package kr.ac.hansung.cse.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
public class CartItem implements Serializable{
//version id
private static final long serialVersionUID = -7296960050350583877L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private Cart cart;
private Product product;
private int quantity;
private double totalPrice;
}
-Product.java
public class Product implements Serializable{ ... }
CartItem과 Cart 구현 ( N:1 관계 )
-CartItem.java (Many To One)
public class CartItem implements Serializable{
...
@ManyToOne
@JoinColumn(name="cartId")
private Cart cart;
@ManyToOne
@JoinColumn(name="productId")
private Product product;
...
}
-Cart.java (One To Many)
public class Cart implements Serializable {
...
// mappedBy 쓸때 참조자 이름 반드시 일치
@OneToMany(mappedBy="cart",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<CartItem> cartItems = new ArrayList<CartItem>();
...
}
cascade=CascadeType.ALL
- Cart가 제거되면 CartItem도 제거된다.
mappedBy="cart"
- 이 컬럼은 Table에 생성되지 않고, Cart를 참조한다.
OneToMany
- default : Lazy
ManyToOne
- default : Eager (데이터를 바로 읽어온다)
User와 Cart 구현 (1:1 관계)
-User.java (One To One)
public class User {
...
@OneToOne(optional=false, cascade=CascadeType.ALL)
@JoinColumn(unique=true)
private Cart cart;
...
}
지금의 코드를 따라가면
- User 호출 > CartItem 호출> Cart 호출 > CartItem 호출 > Cart ...
위와같은 사이클이 생기는것을 방지해야한다.
사이클 방지 @JsonIgnore
-pom.xml 에 dependency추가
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
-CartItem.java
public class CartItem implements Serializable{
...
@ManyToOne
@JoinColumn(name="cartId")
@JsonIgnore
private Cart cart;
...
}
Cart를 Serialization할때 CartItem Json 을 무시하게되어 사이클이 형성되는것을 방지한다.
@JsonIgnore
- JSON 직렬화, 역직렬화에서 속성을 무시하는데 사용된다.
- @JsonIgnore 어노테이션은 클래스의 속성(필드, 멤버변수) 수준에서 사용한다.
dao-context.xml 의 속성을 create로 수정하여 table생성되는것 확인
<prop key="hibernate.hbm2ddl.auto">create</prop>
mysql에서 DB확인
테이블 정상적으로 생성 완료되었다.
cart테이블에 cartItems는 없는것을 확인 할 수 있다.
모든 테이블과 컬럼이 잘 생성된 것을 확인 할 수 있다.
Model이 생성되었으니 DAO와 Service 생성
CartService.java 생성
- Service는 @Service를 사용
package kr.ac.hansung.cse.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.ac.hansung.cse.dao.CartDao;
import kr.ac.hansung.cse.model.Cart;
@Service
public class CartService {
@Autowired
private CartDao cartDao;
public Cart getCartById(int cartId) {
return cartDao.getCartById(cartId);
}
}
CartDao.java 생성
- DAO는 @Repository, @Transactional 사용.
package kr.ac.hansung.cse.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import kr.ac.hansung.cse.model.Cart;
@Repository
@Transactional
public class CartDao {
@Autowired
private SessionFactory sessionFactory;
public Cart getCartById(int cartId) {
Session session = sessionFactory.getCurrentSession();
return (Cart) session.get(Cart.class, cartId);
}
}
CartItemService.java 생성
package kr.ac.hansung.cse.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import kr.ac.hansung.cse.dao.CartItemDao;
import kr.ac.hansung.cse.model.Cart;
import kr.ac.hansung.cse.model.CartItem;
@Service
public class CartItemService {
@Autowired
private CartItemDao cartItemDao;
public void addCartItem(CartItem cartItem) {
cartItemDao.addCartItem(cartItem);
}
public void removeCartItem(CartItem cartItem) {
cartItemDao.removeCartItem(cartItem);
}
public void removeAllCartItems(Cart cart) {
cartItemDao.removeAllCartItems(cart);
}
public CartItem getCartItemByProductId(int cartId, int productId) {
return cartItemDao.getCartItemByProductId(cartId, productId);
}
}
CartItemDao.java 생성
package kr.ac.hansung.cse.dao;
import java.util.List;
import javax.persistence.TypedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import kr.ac.hansung.cse.model.Cart;
import kr.ac.hansung.cse.model.CartItem;
@Repository
@Transactional
public class CartItemDao {
@Autowired
private SessionFactory sessionFactory;
public void addCartItem(CartItem cartItem) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(cartItem);
session.flush();
}
public void removeCartItem(CartItem cartItem) {
Session session = sessionFactory.getCurrentSession();
session.delete(cartItem);
session.flush();
}
public void removeAllCartItems(Cart cart) {
List<CartItem> cartItems = cart.getCartItems();
for(CartItem item : cartItems) {
removeCartItem(item);
}
}
@SuppressWarnings("unchecked")
public CartItem getCartItemByProductId(int cartId, int productId) {
Session session = sessionFactory.getCurrentSession();
TypedQuery<CartItem> query = session.createQuery("from CartItem where cart.id=? and product.id=?");
query.setParameter(0, cartId);
query.setParameter(1, productId);
return(CartItem) query.getSingleResult();
}
}
public void removeAllCartItems(Cart cart) {
List<CartItem> cartItems = cart.getCartItems();
for(CartItem item : cartItems) {
removeCartItem(item);
}
}
에서 cart를 읽으면
@OneToMany(mappedBy="cart",cascade=CascadeType.ALL, fetch=FetchType.EAGER) // mappedBy 쓸때 참조자 이름 반드시 일치
private List<CartItem> cartItems = new ArrayList<CartItem>();
Cart.java의 fetch=FetchType.EAGER 속성에 에 의해 cartItem까지 같이 읽어온다.
작동을 확인하기 위해 dao-context.xml에서 hibernate의 속성을 update 로 수정한다.
<prop key="hibernate.hbm2ddl.auto">update</prop>
RegisterController.java 수정
@RequestMapping(value="/register", method=RequestMethod.POST)
public String registerUserPost(@Valid User user, BindingResult result, Model model) {
if(result.hasErrors()) {
return "registerUser"; // 재 입력 페이지
}
List<User> userList = userService.getAllUsers();
for(int i=0; i<userList.size(); i++) {
if(user.getUsername().equals(userList.get(i).getUsername())) {
// userName이 값으면
model.addAttribute("usernameMsg", "Username already exist");
return "registerUser";
}
}
user.setEnabled(true);
if(user.getUsername().equals("admin"))
user.setAuthority("ROLE_ADMIN");
else
user.setAuthority("ROLE_USER");
Cart cart = new Cart();
user.setCart(cart);
userService.addUser(user);
return "registerUserSuccess";
}
cart가 널값이면 오류가 발생할 수 있으므로 RegisterController.java 수정하여 cart를 생성하고 set해준다.
-User.java
@OneToOne(optional=false, cascade=CascadeType.ALL)
@JoinColumn(unique=true)
private Cart cart;
- optional = false : 반드시 존재
- cascade=CascadeType.ALL : User생성시 cart도 함께 저장.
-실행화면
https://velog.io/@kdhyo/JavaTransactional-Annotation-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-26her30h
@JsonIgnore, @JsonIgnoreProperties, @JsonIgnoreType차이점 (velog.io)
'Study > Spring' 카테고리의 다른 글
[Spring] Store - Cart View / AngularJS - RestAPI / 오류해결 (0) | 2022.09.02 |
---|---|
[Spring] Store - Cart View (장바구니) / AngularJS - RestAPI (0) | 2022.08.25 |
[Spring] Store - Product Detail (0) | 2022.08.20 |
[Spring] Store - Register User / Spring security (0) | 2022.08.18 |
[Spring] 파일 업로드 중복 제거 - UUID (0) | 2022.08.13 |