방법이 두가지 있다.

@Transactional
public void deleteById(Integer id) {
    // 방법 1
    // 쿼리를 직접 작성
    Query query = em.createQuery("delete from Board b where b.id = :id");
    query.setParameter("id", id);
    query.executeUpdate();
}

@Transactional
public void deleteById2(Integer id) {
    // 방법 2
    // 조회하고 삭제
    Board board = findById(id); // id로 조회를 먼저 해주고
    em.remove(board); // 있으면 삭제 없으면 null 값이 들어간다.
}

2번 테스트

@Test
public void deleteById2_test() {

    // 찾아서 딜리트 하였으나 딜리트는 트랜잭션이 종료될 때 쿼리가 날아간다.
    // 하지만 쿼리가 발동하지 않는데 이는 트랜잭션이 자식 트랜잭션이라 그렇다
    // DataJpaTest 가 트랜잭션을 가지고 있는데, deleteById가 또 트랜잭션을 가지고 있음
    // 그러면 DataJpaTest 의 트랜잭션만 실행되고 딜리트의 트랜잭션은 무시된다.
    // 그래서 딜리트의 트랜잭션이 무시되었으니 트랜잭션이 종료되지 않아서 딜리트가 실행이 안됨
    // 삭제만 이렇다
    int id = 1;
    boardPersistRepository.deleteById2(id);

    // 쿼리를 강제로 날려서 테스트 해볼 수 있다.
    // 플러쉬 해서 강제로 버퍼로 쿼리내용을 흘려보낸다.
    em.flush();
}

트랜잭션이 발동되는 방식

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

// 트랜잭션 관리자를 주입받아야 함
private PlatformTransactionManager transactionManager;

public void someMethod() {
    // 트랜잭션 정의를 생성
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    // 트랜잭션 속성 설정 (예: 격리 수준, 읽기 전용 등)
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    // 트랜잭션을 시작
    TransactionStatus status = transactionManager.getTransaction(def);
    try {
        // 비즈니스 로직 수행
        // 데이터베이스 조작

        // 트랜잭션 커밋
        transactionManager.commit(status);
    } catch (Exception ex) {
        // 예외 발생 시 롤백
        transactionManager.rollback(status);
        throw ex;
    }
}
  1. 트랜잭션의 시작: @Transactional 어노테이션이 적용된 메소드가 호출되면, 스프링은 트랜잭션을 시작합니다. 이때 새로운 트랜잭션을 시작하거나 이미 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션에 참여합니다.
  2. 메소드 실행: @Transactional 어노테이션이 적용된 메소드가 실행됩니다. 이 메소드 안에서 데이터베이스 조작이 이루어질 수 있습니다.
  3. 커밋 또는 롤백: 메소드 실행이 성공적으로 완료되면, 스프링은 트랜잭션을 커밋합니다. 이는 메소드 안에서 수행된 모든 데이터베이스 조작이 영구적으로 반영됨을 의미합니다. 그러나 만약 예외가 발생하면, 스프링은 트랜잭션을 롤백하고 이전 상태로 되돌립니다. 따라서 어떠한 조작도 데이터베이스에 영향을 주지 않게 됩니다.
  4. 트랜잭션 종료: 커밋 또는 롤백이 완료되면 트랜잭션은 종료됩니다. 이는 데이터베이스 커넥션도 반환됨을 의미합니다.

트랜잭션이 종료될 때 쿼리가 실행되는데, DataJpaTest와 deleteById는 모두 트랜잭션을 가지고 있습니다. 그러나, DataJpaTest의 트랜잭션이 우선적으로 실행되어, deleteById의 트랜잭션이 무시되고 따라서 삭제 쿼리가 실행되지 않습니다. 이는 삭제에만 해당됩니다.

트랜잭션이 실행되면 트랜잭션이 붙어 있는 메소드가 종료되어야 commit이 발생하는데, DataJpaTest가 트랜잭션을 가지고 있기 때문에 deleteById의 트랜잭션이 종료되지 않습니다. 이 때문에 테스트 단계에서 삭제 쿼리가 실행되지 않습니다.