진취적 삶
8.7 트랜잭션 처리 본문
이메일 인증 시점에 테이블의 데이터를 변경하는 기능은 다음 코드처럼 회원정보에서
이메일을 수정하고 인증 상태를 변경하는 두 쿼리를 실행
jdbcTemplate.update("update MEMBER set EMAIL = ?" ,email);
jdbcTemplate.update("insert into EMAIL_AUTH values(?,'T')" ,email);
중복이나 코드를 잘못 수정/배포 해서 두번째 쿼리에서 문제가 생겼을때
첫번째 ㅋ쿼리 실행 결과도 취소되야 올바른 상태를 유지한다.
두개 이상의 쿼리를 한 작업으로 실행해야 할 때 사용하는 것이 트랜잭션이다 .
트랜잭션은 여러 쿼리를 논리적으로 하나의 작업으로 묶어준다 .
트랜잭션으로 묶인 쿼리 중 하나라도 실패하면 전체 쿼리를 실패로 간주하고 실패 이전에 실행한
쿼리를 취소한다.
쿼리 실행 결과를 DB를 기존 상태로 되돌리는 것을 롤백 (rollback)이라고 부른다.
트랜잭션으로 묶인 모든 쿼리가 성공해서 쿼리 결과를 DB에 실제로 반영하는 것을 커밋(commit)이라고 한다.
JDBC는 Connection 의 setAutoCommit 을 이용해서 트랜잭션을 시작하고 commit() 과 rollback()을
이용해서 트랜잭션을 커밋 하거나 롤백 한다.
Connection conn =null
try {
conn =DriverManager.getConnection(jdbcUrl,user,pw);
conn.setAutoCommit(false) // 트랜잭션 범위 시작
conn.commit();
} catch(SQLException ex) {
if(conn!=null)
try {conn.rollback() ;} catch(SQLExceptio e) {}
} finally {
if (conn!=null)
try{conn.close();} catch(SQLException e){}
}
스프링이 제공하는 트랜잭션 기능을 사용하면 짱짱 쉽게 해결 가능
8.7.1 @Transactional 을 이용한 트랜잭션 처리
@Transactional 사용하면 트랜잭션 범위를 매우 쉽게 지정할수 있다.
App.ctx
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource());
return tm;
}
구현 기술에 상관없이 동일한 방식으로 트랜잭션을 처리하기 위해 이 인터페이스 사용
@EnableTransactionManagement 은 @Transactional 이 붙은 메서드를 트랜잭션 범위에서
실행 하는 기능을 활성화 한다.
public class ChangePasswordService {
private MemberDao memberDao;
@Transactional
public void changePassword(String email,String oldPwd, String newPwd) throws MemberNotFoundException, WrongIdPasswordException {
Member member = memberDao.selectByEmail(email)
....
}
public class MainForCPS {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppCtx.class);
ChangePasswordService cps = ctx.getBean("changePwdSvc",ChangePasswordService.class);
try {
cps.changePassword("hp0724@naver.com", "1234", "1111");
System.out.println("암호를 변경했습니다");
}catch(MemberNotFoundException e) {
System.out.println("회원 데이터가 존재하지 않습니다");
}catch(WrongIdPasswordException e) {
System.out.println("암호가 올바르지 않습니다");
}
ctx.close();
}
}
8.7.2 @Transactional 과 프록시
스프링은 @Transactional 을 이용해서 트랜잭션을 처리하기 위해 내부적으로 AOP를 사용한다.
8.7.3 @Transactional 적용 메서드의 롤백 처리
try {
cps.changePassword("hp0724@naver.com", "1234", "1111");
System.out.println("암호를 변경했습니다");
}catch(MemberNotFoundException e) {
System.out.println("회원 데이터가 존재하지 않습니다");
}catch(WrongIdPasswordException e) {
System.out.println("암호가 올바르지 않습니다");
}
WrongIdPasswordException 이 발생했을때 트랜잭션이 롤백되는것을 알수 있다.
JdbcTemplate 의 기능을 실행하는 도중 익셉션이 발생해도 프록시는 트랜잭션을 롤백한다.
RuntimeException을 상속 받는 경우 트랜잭션을 롤백
8.7.6 트랜잭션 전파
public class SomeService{
private AnyService anyService;
@Transactional
public void some() {
anyService.any() ;
}
public void setAnyService (AnyService as) {
this.anyService = as ;
}
}
public class AnyService{
@Transactional
public void any(){...}
}
@Configuration
@EnableTransationManagement
public class Config{
@Bean
public SomeService some() {
SomeService some = new SomeService();
some.setAnyService(any());
return some.
}
@Bean
public AnyService any() {
return new AnyService();
}
}
SomeService ,AnyService 둘다 @Transactional 적용함
두 클래스에 대해 프록시가 생성
SomeService some() 메서드 실행하면 트랜잭션 시작되고
AnyService any() 메서드 실행하면 트랜잭션 시작된다.
그런데 some() 안에 any() 메서드 호출되고 있음 이경우 트랜잭션 처리 어케?
@Transactional propagation 속성은 기본값이 propagation.REQUIRED 이다 .
REQUIRED 현재 진행중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고 존재하지 않으면
새로운 트랜잭션을 생성 .
'스프링 5 프로그래밍 입문 > 8.DB 연동' 카테고리의 다른 글
8.3 DataSource 설정 (0) | 2023.09.11 |
---|---|
8.4 JdbcTemplate 을 이용한 쿼리 실행 (0) | 2023.09.11 |
8.5 MemberDao 테스트하기 (0) | 2023.09.11 |
8.6 스프링의 익셉션 변환 처리 (0) | 2023.09.11 |
8.8 전체 기능 연동한 코드 실행 (0) | 2023.09.11 |