스프링 부트와 JPA 활용1 강의를 듣고 정리한 내용입니다.
JPA 활용1의 마지막 강의다.
MVC 기능을 주로 다룰 것 같아 배울 내용 보다는 구현이 많을거라 예상했는데 배울 것이 많았다.
폼 객체를 이용해서 화면 계층과 서비스 계층을 명확하게 분리한다.
엔티티로 화면 입력을 받게되면 여의치 않은 예외상황이 발생할 수 있다.
실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다.
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수 입니다")
private String name;
private String city;
private String street;
private String zipcode;
}
@Controller
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping(value = "/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/createMemberForm";
}
@PostMapping(value = "/members/new")
public String create(@Valid MemberForm form, BindingResult result) {
if (result.hasErrors()) {
return "members/createMemberForm";
}
Address address = new Address(form.getCity(), form.getStreet(),
form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return "redirect:/";
}
}
영속 컨텍스트가 더이상 관리하지 않는 엔티티.
정확히는 기존 DB에 대한 식별자를 가지고 있으나 영속성 컨텍스트에 등록되지 않은 엔티티를 말한다.
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
영속성 컨텍스트에서 엔티티를 다시 조회한 후에 데이터를 수정한다.
=> 트랜잭션 커밋 시점에 변경 감지(Dirty Checking) 후 DB에 UPDATE SQL을 자동으로 실행한다.
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}
병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.
변경감지 기능을 사용하면 원하는 속성만 선택해 변경할 수 있지만,
병합을 사용하면 없는 속성을 포함한 모든 속성이 변경되어 null로 업데이트할 위험이 있다.
=> 엔티티를 변경할 때는 항상 변경 감지를 사용한다.
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
/**
* 상품 수정, 권장 코드
*/
@PostMapping(value = "/items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form) {
itemService.updateItem(form.getId(), form.getName(), form.getPrice());
return "redirect:/items";
}
}
package jpabook.jpashop.service;
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
/**
* 영속성 컨텍스트가 자동 변경
*/
@Transactional
public void updateItem(Long id, String name, int price) {
Item item = itemRepository.findOne(id);
item.setName(name);
item.setPrice(price);
}
}