탐구개발
MySQL과 JPA를 사용하는 transaction 테스트 코드 본문
TransactionManager 설정 필요여부
master, slave 분기를 위한 데이터베이스 설정 시
DataSource 뿐 아니라 JpaTransactionManager도 함께 설정하는 예제가 많았습니다.
@EnableTransactionManagement
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@EnableJpaRepositories(basePackages = {"com.test.somoimapi"})
(생략)
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource(dataSource));
entityManagerFactoryBean.setPackagesToScan("com.test.somoimapi");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
return entityManagerFactoryBean;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(entityManagerFactory);
return tm;
}
하지만 실제로 테스트 해봤을 때 JpaTransactionManager 설정 없이도 동작하는 것을 확인했고
JpaTransactionManager 수동 설정 필요여부에 대한 자료를 찾아보았지만 찾을 수 없었습니다.
불필요한 코드로 판단해서 추가하지 않았고
대신 테스트코드를 작성하여 의도한대로 동작하는지 확인했습니다.
분산 트랜잭션같은 복잡한 요구사항이 없기 때문에
단순하게 로컬 JPA 트랜잭션이 동작하는지만 확인했습니다.
JpaTransactionManagerTestService.java
@Service
@RequiredArgsConstructor
public class JpaTransactionManagerTestService {
private final MeetingRepository meetingRepository;
private final UserRepository userRepository;
public static final String THROW_EXCEPTION_KEYWORD = "예외발생";
public void saveWithoutTransactional(Meeting meeting, User user) {
meetingRepository.save(meeting);
userRepository.save(user);
throwException(user);
}
@Transactional
public void saveWithTransactional(Meeting meeting, User user) {
meetingRepository.save(meeting);
userRepository.save(user);
throwException(user);
}
private void throwException(User user) {
if (THROW_EXCEPTION_KEYWORD.equals(user.getName())) {
throw new RuntimeException(user.getName());
}
}
}
참고로 내부 구현으로 SimpleJpaRepository에 @Transactional이 적용되어있습니다.
JpaTransactionManagerTestServiceTest.java
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureTestDatabase
public class JpaTransactionManagerTestServiceTest {
@Autowired
private JpaTransactionManagerTestService jpaTransactionManagerTestService;
@Autowired
private MeetingRepository meetingRepository;
@Autowired
private UserRepository userRepository;
@AfterEach
public void deleteAll() {
meetingRepository.deleteAll();
userRepository.deleteAll();
}
@Test
public void transactional이_없을_경우_에러가_없다면_롤백되지않는다() {
// given
long expectedTotalPostCount = 1;
User User = user("에러없음");
Meeting meeting = meeting();
// when
jpaTransactionManagerTestService.saveWithoutTransactional(meeting, User);
// then
assertThat(meetingRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
assertThat(userRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
}
@Test
public void transactional이_없을_경우_에러가_있어도_롤백되지않는다() {
// given
long expectedTotalPostCount = 1;
User User = user(THROW_EXCEPTION_KEYWORD);
Meeting meeting = meeting();
// when
assertThrows(
RuntimeException.class,
() -> jpaTransactionManagerTestService.saveWithoutTransactional(meeting, User)
);
// then
assertThat(meetingRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
assertThat(userRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
}
@Test
public void transactional이_있을_경우_에러가_없다면_롤백되지않는다() {
// given
long expectedTotalPostCount = 1;
User User = user("에러없음");
Meeting meeting = meeting();
// when
jpaTransactionManagerTestService.saveWithTransactional(meeting, User);
// then
assertThat(meetingRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
assertThat(userRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
}
@Test
public void transactional이_있을_경우_에러가_있다면_롤백된다() {
// given
long expectedTotalPostCount = 0;
User user = user(THROW_EXCEPTION_KEYWORD);
Meeting meeting = meeting();
// when
assertThrows(
RuntimeException.class,
() -> jpaTransactionManagerTestService.saveWithTransactional(meeting, user)
);
// then
assertThat(meetingRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
assertThat(userRepository.findAll().size())
.isEqualTo(expectedTotalPostCount);
}
private User user(String name) {
return User.builder()
.name(name)
.build();
}
private Meeting meeting() {
return Meeting.builder()
.date(LocalDate.now())
.groupId(1L)
.memberId(1L)
.build();
}
}
'프로젝트' 카테고리의 다른 글
MySQL과 JPA를 사용하는 master, slave 설정 - AOP 방식 (0) | 2023.11.12 |
---|---|
MySQL과 JPA를 사용하는 master, slave 설정 - @Transactional 방식 (0) | 2023.11.12 |
MySQL과 JPA를 사용하는 DB 설정 (0) | 2023.11.12 |
IntelliJ GitHub 연동없이 로컬 Git Repository 설정 (0) | 2023.11.09 |
IntelliJ에서 Spring Boot 프로젝트 JAR 생성하기 (0) | 2023.11.09 |