1.스프링 로직(비즈니스 로직)에 애너테이션 추가
2.AOP를 이용해서 비즈니스로직에서 트랜잭션 적용코드를 감추는 방식
@Configuration
@EnableTransactionManagement
public class JpaConfig{
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
final LocalContainerEntityManagerFactoryBean em =
new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
...
...
return em;
}
// (1)
@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
...
...
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager
= new JpaTransactionManager(); // (2)
transactionManager.setEntityManagerFactory(
entityManagerFactoryBean().getObject() );
return transactionManager;
}
}
데이터베이스 커넥션 정보를 담는Datasource가 필요함
Spring에서 트랜잭션은 기본적으로 PlatformTransactionManager에 의해 관리,
해당 인터페이스를 구현해서 데이터 액세스 기술에 맞게 유연하게 트랜잭션을 적용 할수 있도록 추상화 되어있음
@Transactional 애너테이션을 트랜잭션이 필요한 영역에 추가해 주면 된다.
MemberService클래스에 트랜잭션을 적용한 코드 일부
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional // (1)
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public Member createMember(Member member) {
verifyExistsEmail(member.getEmail());
return memberRepository.save(member);
}
}
rollback 동작 유무 확인
예외 발생시 rollback이 잘 되는지 확인 하고 싶을땐
강제로 예외가 발생하도록 수정을 하면된다
public Member createMember(Member member) {
verifyExistsEmail(member.getEmail());
Member resultMember = memberRepository.save(member);
//강제로 예외가 발생하도록 처리한 부분
if (true) {
throw new RuntimeException("Rollback test");
}
return resultMember;
}
@Transactional 애너테이션을 추가할때 트랜잭션 적용
클래스 레벨에만 @Transactional이 적용된 경우
->클래스 레벨의 @Transactional 애너테이션이 메서드에 일괄적용
클래스 레벨과 메서드 레벨이 함께 적용된 경우
->메서드 레벨의 @Transactional이 적용된다.
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.*;
import java.util.HashMap;
import java.util.Map;
// (1)
@Configuration
public class TxConfig {
private final TransactionManager transactionManager;
// (2)
public TxConfig(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Bean
public TransactionInterceptor txAdvice() {
NameMatchTransactionAttributeSource txAttributeSource =
new NameMatchTransactionAttributeSource();
// (3)
RuleBasedTransactionAttribute txAttribute =
new RuleBasedTransactionAttribute();
txAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// (4)
RuleBasedTransactionAttribute txFindAttribute =
new RuleBasedTransactionAttribute();
txFindAttribute.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRED);
txFindAttribute.setReadOnly(true);
// (5)
Map<String, TransactionAttribute> txMethods = new HashMap<>();
txMethods.put("find*", txFindAttribute);
txMethods.put("*", txAttribute);
// (6)
txAttributeSource.setNameMap(txMethods);
// (7)
return new TransactionInterceptor(transactionManager, txAttributeSource);
}
@Bean
public Advisor txAdvisor() {
// (8)
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.codestates.coffee.service." +
"CoffeeService.*(..))");
return new DefaultPointcutAdvisor(pointcut, txAdvice()); // (9)
}
}
1.@Configuration애너테이션 추가, 클래스정의
2.TransactionManager 객체가 필요 -> 해당객체를 DI받는다
3.트랜잭션 어드바이스용 TransactionInterceptor빈 등록
-트랜잭션 애트리뷰트 지정
-트랜잭션 적용할 메서드에 애트리뷰트 매핑
-TransactionInterceptor 객체 생성
4.Advisor객체 생성