@PersistenceContext
EntityManager em;
Spring 프레임워크에서는 DB의 트랜잭션 개념을 애플리케이션에 적용할 수 있도록 트랜잭션 관리자를 제공합니다.
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
...
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
...
}
예시 코드 처럼 @Transactional 애너테이션을 클래스 가장상단이나 메서드에 추가하면 쉽게 트랜잭션 개념을 적용(트랜잭션 환경 설정) 할 수 있습니다.
readOnly = true 옵션인 @Transactional을 덮어쓰게 되어 readOnly = false 옵션으로 적용됩니다.readOnly = true 옵션(최적화)@Test
@Transactional
@Rollback(value = false) // 테스트 코드에서 @Transactional 를 사용하면 테스트가 완료된 후 롤백하기 때문에 false 옵션 추가
@DisplayName("메모 생성 성공")
void test1() {
Memo memo = new Memo();
memo.setUsername("Robbert");
memo.setContents("@Transactional 테스트 중!");
em.persist(memo); // 영속성 컨텍스트에 메모 Entity 객체를 저장합니다.
}트랜잭션이 적용되어 DB 작업이 성공했습니다.
💡@Disabled를 test메서드에 사용하면 이 Test는 더이상 하지 않겠다는 것으로 오류가 나는 테스트라도 테스트가 수행이 되지 않아 초록색으로 나온다.
@Test
@DisplayName("메모 생성 실패")
void test2() {
Memo memo = new Memo();
memo.setUsername("Robbie");
memo.setContents("@Transactional 테스트 중!");
em.persist(memo); // 영속성 컨텍스트에 메모 Entity 객체를 저장합니다.
}
트랜잭션이 적용되지 못해 작업이 취소되었습니다.(오류 발생)
즉, JPA를 사용하여 DB에 데이터를 저장, 수정, 삭제 하려면 트랜잭션 적용이 반드시 필요합니다.
조회 작업은 단순하게 데이터를 읽기만 하기 때문에 트랜잭션 적용이 필수는 아닙니다.
다만 조회의 경우에도 트랜잭션 환경이 필요한 경우가 있을 수 있기 때문에 조회 작업 기능만 존재하는 메서드일 경우에만 앞서 본 예시처럼 readOnly = true 옵션이 설정된 @Transactional을 적용하면 좋습니다.
스프링 컨테이너 환경에서는 영속성 컨텍스트와 트랜잭션의 생명주기가 일치합니다.
쉽게 설명하자면 트랜잭션이 유지되는 동안은 영속성 컨텍스트도 계속 유지가 되기 때문에 영속성 컨텍스트의 기능을 사용할 수 있습니다.
따라서 앞에서 작성한 테스트 코드 메서드에 트랜잭션이 적용되지 않았기 때문에 영속성 컨텍스트가 유지되지 못해 오류가 발생했었습니다.
⚠️ Spring은 어떻게 Service 부터 Repository 까지 Transaction을 유지할 수 있는 걸까요?
@Transactional에서 트랜잭션 전파 옵션을 지정할 수 있습니다.
트랜잭션 전파의 기본 옵션은 REQUIRED 입니다.
REQUIRED 옵션은 부모 메서드에 트랜잭션이 존재하면 자식 메서드의 트랜잭션은 부모의 트랜잭션에 합류하게됩니다.

@Transactional
public Memo createMemo(EntityManager em) {
Memo memo = em.find(Memo.class, 1);
memo.setUsername("Robbie");
memo.setContents("@Transactional 전파 테스트 중!");
System.out.println("createMemo 메서드 종료");
return memo;
}
@SpringBootTest
public class TransactionTest {
@PersistenceContext
EntityManager em;
@Autowired
MemoRepository memoRepository;
@Test
@Transactional
@Rollback(value = false)
@DisplayName("트랜잭션 전파 테스트")
void test3() {
memoRepository.createMemo(em);
System.out.println("테스트 test3 메서드 종료");
}
}

