현재 프로젝트에서는 JPA 가 아닌 mybatis 를 이용하고 있다. mybatis에서는 트랜젝션을 처리할 수 있는 기능이 없기 때문에 스프링의 @transactional 어노테이션을 이용하여, 트랜젝션 관리를 별도로 해주어야 한다.
공식문서에 따르면 총 3가지 방법이 있다. 그 중 나는 표준 설정을 하는 방법을 알아보았는데, 그게 가장 쉽고 직관적이기 때문이다.
DataSourceTransactionManager
과 융합되는 것이 좋다. 스프링 트랜잭션 관리자를 한번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. @Transactional
애노테이션과 AOP스타일의 설정 모두 지원한다. 하나의 SqlSession
객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. 마이바티스 스프링 연동모듈은 한번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다.스프링 트랜잭션을 가능하게 하려면, 스프링 설정파일에 DataSourceTransactionManager
를 생성하자.
@Configuration
public class DataSourceConfig {
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
명시된 DataSource
는 스프링을 사용할때 일반적으로 사용한다면 어떠한 JDBC DataSource
도 될수 있다. JNDI룩업을 통해 얻어진 DataSource
뿐 아니라 커넥션 풀링 기능도 포함한다.
트랜잭션 관리자에 명시된 DataSource
가 SqlSessionFactoryBean
을 생성할때 사용된 것과 반드시 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을것이다.
하지만 사실 이마저도 할 필요가 없다. 왜냐하면,Spring Boot 프로젝트에 spring-boot-starter-data-jpa
또는 spring-boot-starter-jdbc
의존성을 추가하면, Spring Boot는 클래스 경로에 있는 데이터베이스 드라이버와 DataSource
구현을 기반으로 DataSource
및 PlatformTransactionManager
빈을 자동으로 구성하기 때문이다.
지금 현재 프로젝트에는 spring-boot-starter-jdbc 의존성이 있기 때문에 안해줘도 된다.
따라서, 트랜젝션을 사용할 준비는 끝났다.
mapper
클래스를 field 로 갖고 있고, 어노테이션 관리를 하는 dao repository
를 생성하여 관리를 해주었다.
예시는 다음과 같다.
package com.ssafy.enjoytrip.user.dao;
import com.ssafy.enjoytrip.user.model.entity.User;
import com.ssafy.enjoytrip.user.model.mapper.UserMapper;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserRepository {
private final UserMapper userMapper;
public Optional<User> selectByUserId(final String userId) {
return userMapper.selectByUserId(userId);
}
@Transactional
public int updateByUser(final User user) {
return userMapper.updateByUser(user);
}
@Transactional
public int insertByUser(final User user) {
return userMapper.insertByUser(user);
}
@Transactional
public void deleteByUserId(final String userId) {
userMapper.deleteByUserId(userId);
}
}
다음과 같이 구성하면서, transaction을 처리할 수 있게 되었다.