
게시글 삭제하기 JPQL
// 직접 조회
@Transactional
public void deleteByIdV2(int id){
Board board = findById(id);
em.remove(board); // PC에 객체 지우고, (트랜잭션 종료 시) 삭제 쿼리를 전송함
}
// 쿼리문 직접 작성
@Transactional
public void deleteById(int id){
Query query = em.createQuery("delete from Board b where b.id = :id");
query.executeUpdate();
}
v2로 해도 되는데 repository에서 이 비즈니스로직을 짜게 되면 재사용이 불가능하며
원천적으로 srp가 안된다.
- DELETE와 트랜잭션:
remove()
메서드로 엔티티를 삭제하더라도, 트랜잭션이 커밋될 때까지 쿼리는 실행되지 않고, 트랜잭션 종료 시점에만 실제로 반영된다.
- 자식 트랜잭션: 부모 트랜잭션(외부 트랜잭션)이 우선 순위를 가지며, 자식 트랜잭션은 부모 트랜잭션에 의존적입니다. 부모 트랜잭션이 완료되기 전에는 자식 트랜잭션이 실행되지 않거나, 롤백되면 자식 트랜잭션도 롤백된다.
- 메타 어노테이션: 어노테이션 위에 적용된 또 다른 어노테이션을 의미하며, 어노테이션의 동작을 확장하거나 속성을 정의하는 역할을 한다.
public void deleteByIdV2(int id) {
em.getTransaction().begin();
try {
Board board = findById(id);
em.remove(board);
} catch (Exception e) {
em.getTransaction().rollback();
}
em.getTransaction().commit();
}
만약 remove를 때린 상태에서 트랜잭션이 종료하기 전에 다시 조회를 하면 그 결과가 다시 db에서 조회를 해서 가져오기 때문에 삭제가 안된 것으로 착각 할 수 있다.
단위 테스트
버전1
@Test
public void deleteByIdV1_test(){
int id = 1;
boardReposiroty.deleteById(id);
}

버전1을 사용하면 정상적으로 delete 쿼리가 전송된다.
버전2
@Test
public void deleteByIdV2_test(){
int id = 1;
boardReposiroty.deleteByIdV2(id);
}

버전2를 사용하면 select 쿼리만 발동되고 delete 쿼리는 발동되지 않는다.
트랜잭션 내부에 있는 쿼리는 트랜잭션이 완료된 이후에 PC에서 DB 로 쿼리가 전송된다.
하지만 JUnit 테스트에서는
@Test내부에도 트랜잭션이 있기 때문에 이중 트랜잭션이 된다. 따라서 delete 메서드 내부의 트랜잭션이 종료되더라도, JUnit 내부의 트랜잭션이 종료되지 않고, JUnit이 종료되면 프로그램 실행이 종료되기 때문에 따로 쿼리가 전달되지 않는다.

단위 테스트를 할 때 쿼리문이 보이지 않아서 테스트가 힘들기 때문에 강제로 쿼리를 날려버리면 된다!!
@Test
public void deleteByIdV2_Test(){
// given
int id = 1;
// when
boardPersistRepository.deleteByIdV2(id);
// 버퍼에 쥐고 있는 쿼리를 즉시 전송
em.flush(); // 강제로 쿼리 쏘기
em.clear(); // Persistent Context 전체를 비워버리는 기능
// then
}

컨트롤러
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable Integer id){
boardPersistRepository.deleteById(id);
return "redirect:/";
}


Share article