본 문서는 인프런의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 (김영한) 강의를 공부하며 작성한 개인 노트입니다.
src/main/java/jpabook.jpashop/controller/에 Controller 클래스 모음
@Controller
public class HomeController {
@RequestMapping("/") //첫화면
public String home() {
return "home"; //home.html로 thymeleaf 파일 찾아감
}
}
Member 엔티티와 MemberForm의 분리:
@Getter @Setter
public class MemberForm {
@NotEmpty(message = "회원 이름은 필수입니다.")
private String name;
private String city;
private String street;
private String zipcode;
}
@Controller
@RequiredArgsConstructor
public class MemberController {
...
@GetMapping("/members/new")
public String createForm(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/createMemberForm";
}
}
@PostMapping("/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:/";
}
@Valid
: MemberForm 클래스에서 @NotEmpty 처리 등이 모두 valid하게 입력되었는지 return "redirect:/"
: 첫페이지로 이동BindingResult
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요"
th:class="${#fields.hasErrors('name')}? 'form-control
fieldError' : 'form-control'">
*{name}
: *을 앞에 붙이면 넘겨 받은 model (여기서는 memberForm)을 참조하여 필드 가져오는 것th:field=
: 타임리프에서 field=에 지정된 필드명을 id와 name으로 자동 지정함<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.address?.city}"></td>
...
?
: null 무시하고 스탑src/main/java/jpabook.jpashop/controller/ItemController
@PostMapping(value = "/items/new")
public String create(BookForm form) {
Book book = new Book();
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
...
}
준영속 엔티티: 영속성 컨텍스트(entity manager)가 더는 관리하지 않는 엔티티 > JPA가 관리하지 않음
(예) update 안의 book 객체
영속 엔티티
@Transactional
public void updateItem(Long itemId, Book param) {
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
}
@Transactional
void update(Item itemParam) { //itemParam: 파라미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(itemParam);
}
병합 동작 방식
(1) merge() 실행
(2) 파라미터로 넘어온 준영속 엔티티의 식별자 값으로 1차 캐시에서 엔티티 조회 (만약 1차 캐시에 엔티티 존재X > 디비에서 조회 후 1차 캐시에 저장)
(3) 조회한 영속 엔티티(mergeMember)에 member 엔티티 값을 넣음
(4) 영속 상태인 mergeMember 반환
null
로 업데이트될 위험 존재.@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";
}
@RequestParam
- form에서 입력받아온 데이터orderService.order(memberId, itemId, count)
- member, item 등 객체를 직접 넣는 것보다 id를 넘기는 것이 더 효율적임주문 목록 검색/취소 html 페이지
<div class="form-group mx-sm-1 mb-2">
<select th:field="*{orderStatus}" class="form-control">
<option value="">주문상태</option>
<option th:each="status : ${T(jpabook.jpashop.domain.OrderStatus).values()}"
th:value="${status}"
th:text="${status}">option
</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2">검색</button>