IT recording...
[스프링 JPA1] 3. 컨트롤러 본문
[원문링크]
https://adorable-aspen-d23.notion.site/JPA1-3-854d982b16f548719baa55ca118d50bf
김영한님의 [실전!스프링부트와JPA활용1 - 웹 어플리케이션개발] 강의를 듣고 작성한 글입니다.
1. MemberController
@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/members/new")
public String createForm(Model model){
model.addAttribute("memberForm",new MemberForm());
return "members/createMemberForm";
}
@PostMapping("/members/new")
public String create(@Valid MemberForm memberForm, BindingResult result){
//@Valid -> validation을 사용하는구나, MemberForm내부에 필수로 사용하게 적어놓은 것을 필수로 받음
//BindingResult -> 에러가 있어도 그 내용을 여기에 담아서 실행이 됨
if(result.hasErrors()){
return "members/createMemberForm";
}
Address address = new Address(memberForm.getCity(), memberForm.getStreet(),memberForm.getZipcode());
Member member = new Member();
member.setUsername(memberForm.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
@GetMapping("/members")
public String list(Model model){
List<Member> members = memberService.findMembers();
model.addAttribute("members",members);
return "members/memberList";
}
}
1-1. MemberForm
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수 입니다.")
private String name; //이름은 필수적으로 입력받도록 함
private String city;
private String street;
private String zipcode;
}
- @NotEmpty
- 필수적으로 받아야 하는 정보를 명시한다.
- @NotEmpty를 설정한 필드가 있을 때 @Valid를 걸어주면 입력되지 않았을 시 에러를 뿜는다.
- 그 에러 메시지를 BindingResult에 받을 수 있음
@PostMapping("/members/new")
public String create(**@Valid MemberForm memberForm**, BindingResult result){
if(result.hasErrors()){
return "members/createMemberForm";
}
...
return "redirect:/";
}
1-2. 폼 객체 사용 이유
- Member를 사용하지 않고 MemberForm을 사용하는 이유가 무엇일까?
- 요구사항이 복잡해지게 되면 Entity에 화면을 처리하기 위한 기능이 증가하며 종속적으로 변한다.
- 유지보수가 어려워진다는 말이다.
- 실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다. → 화면이나 API에 맞는 폼 객체 / DTO를 사용하자.
2. ItemController
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
@GetMapping("/items/new")
public String createForm(Model model){
model.addAttribute("form",new BookForm());
return "items/createItemForm";
}
@PostMapping("/items/new")
public String crate(BookForm bookForm){
Book book = new Book();
book.setName(bookForm.getName()); //실제 설계를 할 때는 setter를 제거하고 create메소드를 사용하자
book.setPrice(bookForm.getPrice());
book.setStockQuantity(bookForm.getStockQuantity());
book.setAuthor(bookForm.getAuthor());
book.setIsbn(bookForm.getIsbn());
itemService.saveItem(book);
return "redirect:/";
}
@GetMapping("/items")
public String list(Model model){
List<Item> items = itemService.findItems();
model.addAttribute("items",items);
return "items/itemList";
}
@GetMapping("items/{itemId}/edit")
public String updateItemForm(@PathVariable("itemId") Long itemId, Model model){
Book item = (Book) itemService.findOne(itemId); //캐스팅하는게 좋지는 않음. 예제의 단순용를 위해 사용
BookForm form = new BookForm();
form.setId(item.getId());
form.setName(item.getName());
form.setPrice(item.getPrice());
form.setAuthor(item.getAuthor());
form.setStockQuantity(item.getStockQuantity());
form.setIsbn(item.getIsbn());
model.addAttribute("form",form);
return "items/updateItemForm";
}
//위의 방식보다는 아래의 방식으로해야 영속성 컨텍스트의 지원을 받을 수 있다.
@PostMapping("items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form){
**itemService.updateItem(form.getId(), form.getName(),form.getPrice(),form.getStockQuantity());**
return "redirect:/items";
}
}
- Controller에서는 가급적 엔티티의 id, 변경할 data만을 넘겨주는 역할만 하도록 하자
- 대신 service에서 일 다 해줌
- 한 트랜잭션 내에서 데이터 변경이 일어나니 원자성, 일관성, 고립성, 지속성을 보장한다.
//merge는 null도 업데이트 해주니, 아래와 같은 변경 감지를 이용하자
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity){
Item findItem = itemRepository.findOne(itemId);
//얘는 영속 상태니까 데이터만 바꿔주면 JPA가 알아서 flush 날려준다.(바뀐애들 업데이트쿼리 디비에 날림)
//원래는 set말고 change메소드 같은 것을 만들어서 사용해야 함(역추적 가능)
findItem.setPrice(price);
findItem.setName(name);
findItem.setStockQuantity(stockQuantity);
}
2-1. 변경 감지와 병합(merge)
- 준영속 엔티티
- 영속성 컨텍스트가 더는 관리하지 않는 엔티티
- 기존 식별자를 가지고 있는 엔티티
- 변경 감지 기능
- 영속성 컨텍스트에서 엔티티를 다시 조회한 후 데이터를 수정한다.
- 트랜잭션 내부에서 수행해야 한다. (service 계층)
- 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)이 동작해서 데이터베이스에 UPDATE SQL 날림
- 서비스 계층에 식별자(id)와 변경할 데이터(parameter/dto)를 전달한다.
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
- 병합 사용
- 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용
- 가급적 사용X (모든 필드를 변경하기 때문에 null 문제 발생 가능)
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}
2-2. BookForm
@Getter @Setter
public class BookForm {
private Long id;
private String name;
private int price;
private int stockQuantity;
private String author;
private String isbn;
}
3. OrderController
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
@GetMapping("/order")
public String createForm(Model model){
List<Member> members = memberService.findMembers();
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 cancel(@PathVariable("orderId") Long orderId){
orderService.cancelOrder(orderId);
return "redirect:/orders";
}
}
'Spring' 카테고리의 다른 글
[Redis] Redis의 자료구조, 명령어 - (2) (0) | 2022.04.01 |
---|---|
[Redis] Redis란? - (1) (0) | 2022.04.01 |
[스프링 JPA1] 2. 도메인 개발 (0) | 2022.02.15 |
[Spring] 개발 중 마주한 오류들 - 1 (0) | 2022.02.15 |
[스프링 JPA1] 1. 요구사항 분석 및 도메인 셜계 (0) | 2022.02.07 |
Comments