DataBase

트랜잭션(Transaction)

개발메모장 2023. 10. 20. 11:24

트랜잭션(Transaction) - 하나의 업무 단위를 의미

예시)

은행 송금 업무 코드가 있다고 가정

son이 kim에게 돈을 송금하면 kim의 잔고는 늘어나고 son의 잔고는 줄어든다.

이런 흐름의 한개의 업무단위를 트랜잭션이라고 한다.

 

참고 블로그

https://velog.io/@ksk5401/DDL-DML-DCL-%EC%9D%B4%EB%9E%80

 


트랜잭션 자동(Auto), 수동(Manual) 처리

트랜잭션은 자동 또는 수동으로 처리가 가능하다.

보통 IDE에서 자동으로 처리해주는 경우가 많다.

인텔리제이 경우 Tx:Auto로 되어있으며 자동으로 반영(COMMIT)해준다는 의미다.

다른 IDE는 확인해봐야함!

업무과정에서 자동이 아닌 우리가 직접 수동적으로 처리하고 싶다면

트랜잭션 수동으로 처리

수동으로 처리하게 되면 트랜잭션이 종료가 됐다는 코드를 실행 시켜줘야한다. (COMMIT, ROLLBACK)

종료를 안시키면 트랜잭션 상태에 계속 머물러 있다.


COMMIT - 반영하기

송금 업무 트랜잭션을 완료 했다고 가정해보자

-- 송금 업무 son -> kim 1000원 송금
UPDATE bank
SET money = 10000
WHERE name = 'son';

UPDATE bank
SET money = 10000
WHERE name = 'kim';
--- 처리 완료 ---

 

이걸 실행시킨 쿼리파일에서 SELCT * FROM bank 해서 보면 적용이 되어 있지만

새 쿼리 파일에서 조회 해보면 아직 적용이 안되어 있는걸 확인할 수 있다.

 

 

COMMIT; 을 실행해 반영하면 정상적으로 DB에 반영이 적용 된다.

-- 송금 업무 son -> kim 1000원 송금
UPDATE bank
SET money = money - 1000
WHERE name = 'son';

UPDATE bank
SET money = money + 1000
WHERE name = 'kim';

-- 반영하기
COMMIT; -- 위 트랜잭션 실행되고 나서 COMMIT 실행

새 쿼리파일에서 확인 결과 잘 적용된 모습


ROLLBACK - 되돌리기

 

만약 son이 kim에게 1000원을 송금하는 과정에 오류가 발생해

son의 계좌에서 -1000만 되고 kim의 계좌는 그대로라고 가정을 해보자

-- 송금 업무 son -> kim 1000원 송금
UPDATE bank
SET money = 10000
WHERE name = 'son';

-------- son이 1000원 송금 하자마자 오류 발생 --------

UPDATE bank
SET money = 10000
WHERE name = 'kim';

그럼 이걸 COMMIT 하면 오류가 난 상태로 반영이 되기 때문에 COMMIT을 하면 안된다!

 

그럼 이럴 때 트랜잭션이 이미 시작된 상황이라서 종료를 시켜줘야하는데

지금 종료를 시키면 오류가 발생된 상태로 종료가 된다.

 

그래서 트랜잭션이 시작되기 전으로 돌리고 싶다면 ROLLBACK 으로 트랜잭션을 종료 시켜주면된다.

-- 송금 업무 son -> kim 1000원 송금
UPDATE bank
SET money = money - 1000
WHERE name = 'son';
--- son의 송금 과정에서 오류 발생 ---
UPDATE bank
SET money = money + 1000
WHERE name = 'kim';

-- 되돌리기
ROLLBACK;

 

@Transactional (스프링프레임워크에서 제공하는 어노테이션)

트랜잭션으로 처리하고자 하는 메소드에 어노테이션 적용

 

예시)

@Transactional
public void tx1(){
	dao.update1(); // son -> kim 에게 송금
	dao.update2(); // kim 계좌에 들어감
}

주의점!

@Transactional사용할 경우 같은 클래스 파일에서 사용하면 안되고 밖에서 메소드를 호출해야한다

대부분 Service 영역에서 처리함

 

이유: 프록시(proxy)를 거치는 경우 같은 클래스 안에서 메소드를 호출하면 문제가 발생한다         ㄴ 자세한건 검색해보기 

 

@Transactional 뿐만 아니라 다른 어노테이션중 프록시(proxy)를 통해 실행되는 경우

다른 클래스에서 메소드를 호출 하면됨

 

프록시(proxy)를 안통한다면 같은클래스에서 메소드 호출해도 무관.


@Transactional 어노테이션에서 ROLLBACK 되는 조건 

@Transactional
public void tx1(){
	dao.update1(); // son -> kim 에게 송금
    
    int c = 1 / 0; // runtimeException (오류가 발생했다고 가정)
    
	dao.update2(); // kim 계좌에 들어감
}

// 트랜잭션 과정중 오류 발생 시 자동으로 ROLLBACK

만약 트랜잭션 과정에서 오류가 발생했다면

자동으로 ROLLBACK 된다.

 

하지만 API 문서를 확인해보면 ROLLBACK 되는 조건이 runtimeException에서만 발생된다.

그래서 모든 Exception에 ROLLBACK을 적용 시키고 싶다면 어노테이션 속성값으로

(rollbackFor = Exception.class) 적용 시키면 된다.

예시)

@Transactional(rollbackFor = Exception.class)
public void tx1(){
	dao.update1(); // son -> kim 에게 송금
    
    int c = 1 / 0; // runtimeException (오류가 발생했다고 가정)
    
	dao.update2(); // kim 계좌에 들어감
}

// 트랜잭션 과정중 오류 발생 시 자동으로 ROLLBACK

보통 트랜잭션은 service 영역에서 처리가 되기 때문에 @Transactional 어노테이션을

service 클래스명에 적용시키면 클래스 파일 내 모든 메소드한테도 적용이 된다.

 

예시)

@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class) 
// 클래스에 트랜잭션 어노테이션이 적용되면 클래스의 모든 메소드들이 적용됨
// service 는 거의 하나의 업무단위를 모아놓은 클래스이기 때문에 클래스에 대게 적용시킴
public class MyService1 {

    private final MyDao6 dao;
    
    public void tx1() {
        dao.update1();
        dao.update2();
    }

}

그리고 컨트롤러에서 메소드 호출해 사용하면 끝~

 

성능향상에 도움을 주는 속성값 - @Transactional(readOnly = true)

이유는 해당 블로그 참고

https://willseungh0.tistory.com/75