if(restStock < 0) {
// exception
throw new NotEnoughStockException("need more stock");
}
- 패키지 생성 -> 클래스 생성
- 클래스 안에서 superclass 생성
- 두번째 항목 제외 모두 선택 후 exception 생성
public class NotEnoughStockException extends RuntimeException{
public NotEnoughStockException() {
super();
}
public NotEnoughStockException(String message, Throwable cause) {
super(message, cause);
}
public NotEnoughStockException(String message) {
super(message);
}
public NotEnoughStockException(Throwable cause) {
super(cause);
}
}
파라미터 가변인자
자바에서는 파라미터 개수가 다르면 다른 메소드로 인식 한다.
파라미터가 한 개인 경우
public void search(String one){}
Map
을 사용하는 경우
public void search(Map<String, String> param){}
List
를 사용하는 경우
public void search(List param){}
VO객체
를 사용하는 경우
public void search(ParamVO param){}
[가변인자]
기호(...)
를 붙여주면 된다.public class Main01 {
public static void main(String[] args) {
test();
test("a");
test(1,"a");
test("a","b");
test(5, new String[] {"a","b","c"});
test(1, true, "홍길동", "이순신", "유성룡");
}
public static void test(String... param) {
System.out.println("test1번 실행");
String[] array = param;
for(String str:param) {
System.out.println(str);
}
System.out.println("----------------------");
}
// 다른 파라미터와 가변인자를 같이 사용하는 경우에는 가변인자를 제일 뒤에 위치시켜야 한다.
// int num 변수가 반드시 있어야 한다.
public static void test(int num, boolean bool, String... param) {
System.out.println("test2번 실행");
System.out.println("num : " +num);
System.out.println("bool : " +bool);
String[] array = param;
for(String str:param) {
System.out.println(str);
}
System.out.println("----------------------");
}
}
영속성 전이
특정 엔티티에 대해 특정한 작업을 수행하면 관련된 엔티티에도 동일한 작업을 수행한다는 의미
아래의 예시를 봅시다. 현재 Parent와 Child는 1:N 연관관계 (@OneToMany, @ManyToOne 사용)를 가진 상태입니다.
위의 코드대로라면 persist 상태로 세 객체를 만들기 위해서 persist()를 세 번 호출해야 합니다.
이런 것이 번거롭기 때문에 우리는 parent객체 하나만으로 child1, child2를 같이 관리하고 싶은 겁니다.
parent하나만 persist 상태로 만들면 이것과 연관관계를 가진 child1, child2가 자동으로 persist가 되도록요.
이때 사용하는 것이 바로 CASCADE
입니다.
persist
가 됩니다. buildscript {
dependencies {
classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
}
}
apply plugin: "com.ewerk.gradle.plugins.querydsl"
dependencies {
implementation 'com.querydsl:querydsl-jpa'
implementation 'com.querydsl:querydsl-apt'
}
//querydsl 추가
def querydslDir = 'src/main/generated'
//def querydslDir = "$buildDir/generated/querydsl"
querydsl {
library = "com.querydsl:querydsl-apt"
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', querydslDir]
}
}
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
configurations {
querydsl.extendsFrom compileClasspath
}
Gradle Tasks에서 프로젝트에 해당하는 폴더 클릭
build 부분 클릭 후 Run Gradle Tasks 클릭
성공, build.gradle -> gradle -> Refresh Gradle Project 를 한번 더 실행해준다.
경로에 맞춰서 폴더가 생성된다.
public enum OrderStatus {
ORDER, CANCEL
}
@Getter @Setter
public class OrderSearch {
private String memberName;
private OrderStatus orderStatus;
}
@Entity
@Getter @Setter
@Table(name = "orders")
public class Order {
@Id @GeneratedValue
@Column(name="order_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
private LocalDateTime orderDate;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems
= new ArrayList<OrderItem>();
// 주문상태(ORDER, CANCEL) -> Enum
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
//////// 연관관계 메서드
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
System.out.println(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
//////// 연관관계 메서드
public static Order createOrder(Member member, OrderItem... orderItems) {
Order order = new Order();
order.setMember(member);
for(OrderItem orderItem : orderItems) {
order.addOrderItem(orderItem);
}
// order 시간, 상태 order table만 가지고 있는 내용 이므로
// 따로 set을 해준다.
order.setOrderStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
// 주문 취소
public void cancel() {
this.setOrderStatus(orderStatus.CANCEL);
// 하나의 오더에 주문별로 오더 아이템부분에 cancel을 날려야 할 수 있어서 반복문 처리
for(OrderItem orderItem : orderItems) {
orderItem.cancel();
}
}
}
@Entity
@Getter @Setter
public class OrderItem {
@Id @GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
private int orderPrice; // 주문가격
private int count; // 주문수량
// 생성 메서드
public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(orderPrice);
orderItem.setCount(count);
// 주문한 만큼 재고 조정
// Item에 있는 stockQuantity 재고 조정
item.removeStock(count);
return orderItem;
}
// 취소
public void cancel() {
//재고수량 원복
getItem().addStock(count);
}
}
@Getter @Setter
public class OrderSearch {
private String memberName;
private OrderStatus orderStatus;
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<form role="form" action="/order" method="post">
<div class="form-group">
<label for="member">주문회원</label>
<select name="memberId" id="member" class="form-control">
<option value="">회원선택</option>
<option th:each="member : ${members}"
th:value="${member.id}" th:text="${member.name}"/>
</select>
</div>
<div class="form-group">
<label for="item">상품명</label>
<select name="itemId" id="item" class="form-control">
<option value="">상품선택</option>
<option th:each="item : ${items}"
th:value="${item.id}" th:text="${item.name}"/>
</select>
</div>
<div class="form-group">
<label for="count">주문수량</label>
<input type="number" name="count" class="form-control" id="count" placeholder="주문 수량을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br />
<div th:replace="fragments/footer :: footer" />
</div>
<!-- /container -->
</body>
</html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<div>
<div>
<form th:object="${orderSearch}" class="form-inline">
<div class="form-group mb-2">
<input type="text" th:field="*{memberName}" class="form-control" placeholder="회원명" />
</div>
<div class="form-group mx-sm-1 mb-2">
<select th:field="*{orderStatus}" class="form-control">
<option value="">주문상태</option>
<option
th:each="status : ${T(com.koreait.jpashop.domain.OrderStatus).values()}"
th:value="${status}" th:text="${status}">option</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2">검색</button>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>회원명</th>
<th>대표상품 이름</th>
<th>대표상품 주문가격</th>
<th>대표상품 주문수량</th>
<th>상태</th>
<th>일시</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${orders}">
<td th:text="${item.id}"></td>
<td th:text="${item.member.name}"></td>
<td th:text="${item.orderItems[0].item.name}"></td>
<td th:text="${item.orderItems[0].orderPrice}"></td>
<td th:text="${item.orderItems[0].count}"></td>
<td th:text="${item.orderStatus}"></td>
<td th:text="${item.orderDate}"></td>
<td><a th:if="${item.orderStatus.name() == 'ORDER'}" href="#"
th:href="'javascript:cancel('+${item.id}+')'"
class="btn btn-danger">CANCEL</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer" />
</div>
<!-- /container -->
</body>
<script>
<!-- 취소 기능 -->
function cancel(id){
let form = document.createElement("form");
form.setAttribute("method","post");
form.setAttribute("action","/orders/" + id + "/cancel");
document.body.appendChild(form);
form.submit();
}
</script>
</html>
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
// request : order
// member 조회, item 조회
// response : order/orderForm
@GetMapping("/order")
public String createForm(Model model) {
// member 조회
List<Member> members = memberService.findMembers();
// item 조회
List<Item> items = itemService.findItems();
model.addAttribute("members",members);
model.addAttribute("items",items);
return "order/orderForm";
}
@PostMapping("/order")
public String order( @RequestParam("memberId") Long memberId,
@RequestParam("itemId") Long itemId,
@RequestParam("count") int count ) {
orderService.order(memberId, itemId, count);
return "redirect:/orders";
}
@GetMapping("/orders")
public String orderList(@ModelAttribute("orderSearch")OrderSearch orderSearch, Model model) {
// 조회
List<Order> orders = orderService.findOrders(orderSearch);
model.addAttribute("orders", orders);
return "order/orderList";
}
// 취소
@PostMapping("/orders/{orderId}/cancel")
public String cancelOrder(@PathVariable("orderId")Long orderId) {
orderService.cancelOrder(orderId);
return "redirect:/orders";
}
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class OrderService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
private final ItemRepository itemRepository;
// 주문
@Transactional
public Long order(Long memberId, Long itemid, int count) {
// 엔티티 조회
// jpa 영속성 컨텍스트 영역의 들어 옴
Member member = memberRepository.findOne(memberId);
Item item = itemRepository.findOne(itemid);
// 주문상품 생성
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
// 주문 생성
// 어떤 사람이 어떤 오더 아이템을 준비 했는지
Order order = Order.createOrder(member,orderItem);
// 주문 저장
orderRepository.save(order);
return order.getId();
}
public List<Order> findOrders(OrderSearch orderSearch) {
return orderRepository.findAll(orderSearch);
}
// 취소
@Transactional
public void cancelOrder(Long orderId) {
Order order = orderRepository.findOne(orderId);
// 취소(update)에 대한 비지니스 로직 처리
order.cancel();
}
}
@Repository
@RequiredArgsConstructor
public class OrderRepository {
private final EntityManager em;
// order 저장
public void save(Order order) {
em.persist(order);
}
// 단건 조회
public Order findOne(Long id) {
return em.find(Order.class, id);
}
// 조회, queryDSL
public List<Order> findAll(OrderSearch orderSearch) {
JPAQueryFactory query = new JPAQueryFactory(em);
QOrder order = QOrder.order;
QMember member = QMember.member;
return query.select(order)
.from(order)
.join(order.member, member) // member : member의 알리아스
// .where(order.orderStatus.eq(orderSearch.getOrderStatus())
// ,member.name.like(orderSearch.getMemberName()))
.where(statusEq(orderSearch.getOrderStatus()),nameLike(orderSearch.getMemberName()))
.fetch()
;
}
private BooleanExpression statusEq(OrderStatus orderStatus) {
if( orderStatus == null) {
return null;
}
return QOrder.order.orderStatus.eq(orderStatus);
}
private BooleanExpression nameLike(String memberName) {
if(memberName == null || memberName.equals("")) {
return null;
}
return QMember.member.name.like(memberName);
}
}