`실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발` 강의를 듣고 정리한 자료입니다
✨ @PathVariable
@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());
...
}
- {itemId} 는 변경될 수 있기 때문에 @PathVariable로 itemId을 가져다 써야 한다
@PostMapping("items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form) {
}
- 근데 필수는 아님 (????)
- 위 예제에서는 form에 itemId가 담겨 오기 때문에 @PathVariable가 따로 필요하지 않다
✨ 핸들러 매개변수 매핑
@RequestParam
- `사용자가 요청 시 전달하는 값`을 핸들러(컨트롤러)의 매개변수로 1:1 매핑 시 사용
@Controller
public class TestController {
@GetMapping("/")
public String getTestPage(@RequestParam("name") String name) {
System.out.println("이름 : " + name);
return "test";
}
}
// 출처: https://galid1.tistory.com/769 [배움이 즐거운 개발자:티스토리]
- 사용자가 `/?name=test`를 전달했을 시 핸들러의 매개변수 name과 "test"를 매핑
@ModelAttribute
- `사용자가 요청 시 전달하는 값`을 오브젝트 형태로 매핑
- 하나가 아닌 여러 개의 매가변수 매핑 시 효과적이다
@Getter
@Setter
public class TestModel {
private String name;
private int age;
}
@RestController
public class TestController {
@GetMapping("/")
public String getTestPage(@ModelAttribute TestModel testModel) {
System.out.println("이름 : " + testModel.getName());
System.out.println("나이 : " + testModel.getAge());
return "test";
}
}
// 출처: https://galid1.tistory.com/769 [배움이 즐거운 개발자:티스토리]
- TestModel 객체는 Setter이 존재 해야 한다
- Model을 사용하는 경우 model에 자동으로 담기기 때문에 생략이 가능하다
@GetMapping("/orders")
public String orderList(@ModelAttribute("orderSearch")OrderSearch orderSearch, Model model) {
// 회원이름, 주문상태
List<Order> orders = orderService.findOrders(orderSearch);
model.addAttribute("orders", orders);
model.addAttribute("orderSearch", orderSearch);
return "order/orderList";
}
- model.addAttribute("orderSearch", orderSearch); 구문 생략 가능
결론
- @RequestParam & @ModelAttribute 모두 사용자가 요청 시 전달하는 값을 매핑해준다
- 그러나 @RequestParam은 1:1로 매핑, @ModelAttribute는 오브젝트 형태로 매핑해준다
- @RequestParam은 전달 값이 한 개 일 때 효과적이고, @ModelAttribute는 전달 값이 여러 개일 때 효과적이다
- @ModelAttribute를 사용하면 전달 값의 인자가 추가될 경우 controller를 건들지 않아도 되어 안전하다
✨ 준영속 엔티티
- 영속성 컨텍스트가 더는 관리하지 않는 엔티티
- 기존에는 `dirty checking`을 통해 변경을 감지해 자동으로 트랜잭션이 끝나는 시점에 db에 반영해주는데, 문제는 준영속 엔티티일 때 발생한다
- 변경 감지 (dirty checking) 는 영속성 컨텍스트에서 관리되는 엔티티만 추적하기 때문이다
- 더티체킹 관련 정리본 : https://ajeong7038.tistory.com/26
- 객체가 DB에 저장될 때 식별자가 생성되는데, 기존 식별자를 가지고 있는 경우 준영속 엔티티가 되어 영속성 컨텍스트(JPA)가 더이상 관리하지 않는다
- id (식별자)가 있는 경우 준영속 엔티티가 된다고 한다
✨ 준영속 엔티티 수정 방법
1. 변경 감지 기능 사용
- 영속성 컨텍스트에서 엔티티를 다시 조회한 후 데이터를 수정하는 방법
- 트랜잭션 안에서 엔티티를 다시 조회, 변경할 값 선택 -> 트랜잭션 커밋 시점에 변경 감지(Dirty Checking)이 동작해 db에 업데이트 쿼리를 날림
동작 방식
1) 식별자로 db에서 찾아와 영속성 상태의 엔티티 찾아오기
2) 값 수정 시 변경 감지 기능으로 알아서 플러시 날려준다.
@Transactional
public Item updateItem(Long itemId, Book param) {
Item findItem = itemRepository.findOne(itemId); // id를 기반으로 실제 db에 있는 영속성 상태의 엔티티 찾아오기
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
itemRepository.save(findItem);
return findItem;
}
2. 병합(merge) 사용
- 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능
- id값(식별자)가 존재하는 경우 em.merge() 사용
동작 방식
1. merge() 실행
2. 준영속 엔티티의 식별자 값 (ex, id)으로 영속 엔티티 조회
3. 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체 (병합)
4. 트랜잭션 커밋 시점에 변경 감지 기능이 동작해 db에 업데이트 쿼리 실행
@PostMapping("items/{itemId}/edit")
public String updateItem(@PathVariable String itemId, @ModelAttribute("form") BookForm form) {
Book book = new Book();
book.setIsbn(form.getIsbn());
book.setId(form.getId());
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/items";
}
주의사항
- 변경 감지 기능을 사용하면 원하는 속성만 선택해서 변경할 수 있지만, 병합을 사용하면 모든 속성이 변경된다 (값이 없을 시 null값으로 대체될 수 있기 때문에 위험하다)
- 최대한 merge 사용하지 말 것
- 외부(ex, controller)에서는 식별자만 넘기고 실제 핵심 비즈니스 서비스에서는 엔티티를 찾는 것부터 설계 (영속 상태로 진행이 가능하다)
✨ 질문
1. Service와 Repository의 관계
- Repository에서 구현되어 있는 메소드들을 이름만 바꿔서 구현...?
2. 사진 업로드 기능이 필요할 때 백엔드...?
3. API 개발
- 강의에서 하는 방식이 API 방식의 개발이 아닌가요 ?.?
4. @PathVariable
- 필수로 필요한 경우?
- 메소드 내부에서 @PathVariable이 붙은 변수를 사용하는 경우 필수 지정인가요?
5. 변경 감지와 병합
- 변경 감지 기능 사용은 변수에 식별자를 통해 (find 등 사용) 영속성 엔티티를 찾는 것이고, 병합은 new로 새 객체를 생성...? 차이점이 뭘까요...
✨ 참고자료
https://velog.io/@sin_0/Spring-model.addAttribute-%EC%82%AC%EC%9A%A9%EB%B2%95
'Spring > JPA' 카테고리의 다른 글
[Spring] 개념 정리 (0) | 2024.01.12 |
---|---|
[Spring] 로그인 (쿠키, 세션) (0) | 2024.01.10 |
[스프링] JPA_S6 : 주문 도메인 개발 (테스트 코드, 리다이렉트, Valid) (1) | 2024.01.08 |
[Spring] 스프링 패턴, JPA 기본 개념과 스프링 패키지 정리 (1) | 2023.11.23 |
[스프링] JPA_S5 : 상품 도메인 개발 (0) | 2023.11.14 |