* 변경감지 == dirty checking
set이후 DB에 따로 작업을 안해도, 스프링JPA가 자동으로 DB에 반영해준다
@RunWith(SpringRunner.class)
@SpringBootTest
public class ItemUpdateTest {
@Autowired
EntityManager em;
@Test
public void updateTest() throws Exception{
Book book = em.find(Book.class, 1L);
//트랜젝션안에서
book.setName("asdfghf");
//set이후 트랜잭션 커밋하면 스프링이 자동으로 DB에 반영해줌 == 변경감지
}
}
예시2) Order-Cancel
public void cancel(){
if(delivery.getStatus()==DeliveryStatus.COMP){
throw new IllegalStateException("이미 배송완료된 상품은 취소가 불가능합니다.");
}
//else
this.setStatus(OrderStatus.CANCEL);
for(OrderItem orderItem : orderItems){
orderItem.cancel();
}
}
-단점 : (JPA가) 준영속 엔티티는 관리안함 -> set만으로 자동DB반영(업뎃) 안해줌
-준영속 엔티티 : 한번이상 DB에 갔다온 얘
아래코드에서 book객체는 새로만들었지만, id기반으로 생각을해보면, 이미 DB에 갔다온 애를 이용해서 세팅하므로 Book은 준영속엔티티다.
public String updateItem(@ModelAttribute("form") BookForm form) {
Book book = new Book();
//스마트하게 복붙 : edit-colum selection mode - shift, ctrl로 조정
//shift+ctrl+u => 대문자로
//보안취약점 : id조작가능
//해결 : 유저가 item권한이 있는지 체크해주는 로직추가
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"; //끝나면 목록으로 ㄱㄱ
}
* 해결1 : 변경감지 사용
ex1) 교재예시
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
1. @Transactional
2. 파라미터로 준영속넘겨
3. 다시조회 후 수정
4. 트랜잭션 커밋 됨-> 변경감지 -> update SQL 실행!
ex2) ItemService.class
@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());
}
*해결 2: 병합(merge) 사용
파라미터의 값으로 모든 필드값을 바꿔치기한다.
item이 영속성으로 바뀌는게아님.
em.merge에서 반환된 것이 영속성컨텍스트임 -> 사용할때 이거 받아서 쓰면됨.
public void save(Item item){
if(item.getId()==null){
em.persist(item); //아이디가없으면(새로생성된객체), 아이템 저장
}
else{
em.merge(item); //이미DB에 등록된객체 -> 업데이트
}
}
내부동작 : 아래코드와 유사하다. return추가.
public Item updateItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
return findItem;
}
- 단점 : NULL가능 -> 머지 쓰지마
가격이 고정이라 set안하는경우 -> 가격이 NULL로 set되서 망한다!
@PostMapping("/items/{itemId}/edit") //itemId는 html에서 넘어오니까 @PathValiable필요X
//@ModelAttribue로 html에서 {form}에 해당하는 객체를 받아온다.
public String updateItem(@ModelAttribute("form") BookForm form) {
Book book = new Book();
//스마트하게 복붙 : edit-colum selection mode - shift, ctrl로 조정
//shift+ctrl+u => 대문자로
//보안취약점 : id조작가능
//해결 : 유저가 item권한이 있는지 체크해주는 로직추가
book.setId(form.getId());
book.setName(form.getName());
// book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
* 리팩토링 (set을쓰지말자, 메소드로관리하라, 추적가능한 설계)
@Transactional
public void updateItem(Long itemId, Book param){
Item findItem = itemRepository.findOne(itemId);
findItem.change(price,name,stockQuantity);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
}
set 남발 =>버그시 어디서 가격을바꾸는지 찾는데 어려움
chagne메소드로 관리 => 관리쉬움(추적가능한 설계)
*리팩토링2(dto 언제사용하나)
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity){
Item findItem = itemRepository.findOne(itemId);
//findItem.change(price,name,stockQuantity); //리팩토링 set남발금지
findItem.setPrice(price);
findItem.setName(name);
findItem.setStockQuantity(stockQuantity);
}
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
Book book = new Book();
//스마트하게 복붙 : edit-colum selection mode - shift, ctrl로 조정
//shift+ctrl+u => 대문자로
//보안취약점 : id조작가능
//해결 : 유저가 item권한이 있는지 체크해주는 로직추가
// 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);
//리팩토링
itemService.updateItem(itemId, form.getName(),form.getPrice(), form.getStockQuantity());
- 더 업데이트할게 많은 경우 해결 : dto class를 만들고, dto를 updateItem 파라미터로 넘기고, dto.set으로 세팅
'Java > Spring-app' 카테고리의 다른 글
주문 목록 검색, 취소 // @ModelAttribute, @PathVariable / 실전편-1 끝! (0) | 2023.07.31 |
---|---|
상품주문 // 컨트롤러 : 인자넘기기만 하라 / 처리는 서비스계층에서 (0) | 2023.07.30 |
상품 수정 구현 // @PathVariable, @ModelAttribute, 위아래편집, 보안취약점, em.persist vs em.merge (0) | 2023.07.18 |
상품 목록 구현 // (0) | 2023.07.18 |
상품등록 // @DiscriminatorColumn (0) | 2023.07.18 |