(책) 자바 웹을 다루는 기술

Chap 25 스프링 트랜잭션 기능 사용하기

EunaSon 2023. 9. 19. 19:38

25.1 트랜잭션 기능 904p                                                      

25.2 은행 계좌 이체를 통한 트랜잭션 기능 905p                  

25.3 스프링의 트랜잭션 속성 알아보기 907p                        

25.4 스프링 트랜잭션 기능 적용해 계좌 이체 실습하기 908p


25.1 트랜잭션 기능

904p

스프링의 트랜잭션 기능은 마이바티스 기능과 연동해서 사용함

XML 파일에서 설정하는 방법 / 애너테이션을 이용하는 방법 두 가지가 있으나 후자가 더 많이 선호됨

 

트랜잭션(Transaction)

: 여러 개의 DML 명령문을 하나의 논리적인 작업 단위로 묶어서 관리하는 것

All(commit, 영구반영) 또는 Nothing(rollback, 모두취소) 방식으로 작업 단위가 처리됨

Service의 각 메서드가 애플리케이션의 단위기능을 수행함,

단위기능 하나가 DAO의 SQL문 하나로 처리되기도 하지만 여러 개의 SQL문을 묶어서 처리하기도 함

여러 SQL문을 묶어서 작업을 처리할 때 어느 하나의 SQL문이라도 잘못되면 이전에 수행한 모든 작업을 취소해야만 작업의 일관성이 유지됨

=> 트랜잭션은 각 단위 기능 수행 시 이와 관련된 DB 연동 작업을 한꺼번에 묶어서 관리한다는 개념임

 

25.2 은행 계좌 이체를 통한 트랜잭션 기능

905p

A가 B에게 500만원을 인터넷 뱅킹으로 계좌이체하는 과정

- 트랜잭션 적용x

A 계좌에서 500만원 인출

> A 계좌에서 500만원 차감 (update)

> A 계좌 잔고 갱신 (commit) --- update문 -> commit

> B 계좌에 500만원 송금

> B 계좌에 500만원 증가시킴 (update)

> B 계좌 잔고 갱신 (commit)  -- update문 -> commit

 

=> 만약 A 작업 후 commit 했는데 B 작업 중 시스템 이상으로 처리되지 못했다면 큰 문제가 됨

 

- 트랜잭션 적용o

A 계좌에서 500만원 인출

> A 계좌에서 500만원 차감 (update)

> B 계좌에 500만원 송금

> B 계좌에 500만원 증가시킴 (update)

> B 계좌 잔고 갱신 (commit) -- update -> update -> commit

 

=> A, B 계좌 잔고 갱신이 모두 이루어진 후에 최종적으로 commit 함

중간에 이상이 발생할 경우 이전 작업을 모두 취소, 즉 롤백하므로 안전한 거래가 가능 & 문제 발생의 소지가 줄어듦

 

25.3 스프링의 트랜잭션 속성 알아보기

907p

▼스프링의 여러 가지 트랜잭션 속성들

속성 기능
propagation 트랜잭션 전파 규칙 설정
isolation 트랜잭션 격리 레벨 설정
readOnly 읽기 전용 여부 설정
rollbackFor 트랜잭션을 롤백할 예외 타입 설정
norollbackFor 트랜잭션을 롤백하지 않을 예외 타입 설정
timeout 트랜잭션 타임아웃 시간 설정

 

▼propagatuib 속성이 갖는 값

의미
REQUIRED - 트랜잭션 필요, 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션 사용
- 트랜잭션이 없으면 새로운 트랜잭션 생성, 디폴트값임
MANDATORY - 트랜잭션 필요
- 진행 중인 트랜잭션이 없는 경우 예외 발생
REQUIRED_NEW - 항상 새로운 트랜잭션 생성
- 진행 중인 트랜잭션이 있는 경우 기존 트랜잭션을 일시 중지시킨 후 새로운 트랜잭션 시작
- 새로 시작된 트랜잭션이 종료되면 기존 트랜잭션 계속 진행
SUPPORTS - 트랜잭션 필요 없음
- 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션 사용
NOT_SUPPORTED - 트랜잭션 필요 없음
- 진행 중인 트랜잭션이 있는 경우 기존 트랜잭션을 일시 중지시킨 후 메서드 실행
- 메서드 실행이 종료되면 기존 트랜잭션 계속 진행
NEVER - 트랜잭션 필요 없음
- 진행 중인 트랜잭션이 있는 경우 예외 발생
NESTED - 트랜잭션 필요
- 진행 중인 트랜잭션이 있는 경우 기존 트랜잭션에 중첩된 트랜잭션에서 메서드 실행
- 트랜잭션이 없으면 새로운 트랜잭션 생성

 

▼isolation 속성이 갖는 값

속성 기능
DEFAULT 데이터베이스에서 제공하는 기본 설정 사용
READ_UNCOMMITED 다른 트랜잭션에서 커밋하지 않은 데이터 읽기 가능
READ_COMMITED 커밋한 데이터만 읽기 가능
REPEATABLE_READ 현재 트랜잭션에서 데이터를 수정하지 않았다면 처음 읽어온 데이터와 두번째 읽어온 데이터가 동일
SERIALIZABLE 같은 데이터에 대해 한 개의 트랜잭션만 수행 가능

 

25.4 스프링 트랜잭션 기능 적용해 계좌 이체 실습하기

908p

SQL Developer로 예금자 계좌 정보를 저장하는 테이블 cust_account 를 생성

create table cust_account (
    accountNO varchar2(20) primary key, -- 계좌번호--
    custName varchar2(50), -- 예금자 이름 --
    balance number(20, 4) -- 계좌 잔고 --
);

'홍길동' 과 '김유신' 의 계좌 정보 insert 후 commit

 

1. 트랜잭션 관련 XML 파일 설정하기

909p

WebContent/WEB-INF/config - action-myBatis.xml, action-service.xml, jdbc.properties 준비

WebContent/WEB-INF - action-servlet.xml, web.xml 준비

 

[ web.xml ]

24장 내용 복붙

 

[ action-servlet.xml ] pro25/WebContent/WEB-INF/action-servlet.xml

- viewResolver 빈 - InternalResourceViewResolver 클래스, 속성 viewClass, prefix, suffix 설정

- accController 빈 - AccountController 클래스, methodNameResolver 속성에 methodResolver 빈 주입, accService 속성에 accService 빈 주입

- methodResolver 빈 - PropertiesMethodNameResolver 클래스, mappings 속성에 key="/account/sendMoney.do" -> sendMoney 메서드 호출 설정

- urlMapping 빈 - SimpleUrlHandlerMapping 클래스, mappings 속성에 key="/account/*.do" -> accController 빈 실행 설정

 

[ action-mybatis.xml] pro25/WebContent/WEB-INF/config/action-mybatis.xml

- accDAO 빈 - AccountDAO 클래스, sqlSession 속성에 sqlSession 빈 주입

- txManager 빈 - DataSourceTransactionManager 클래스, dataSource 속성에 dataSource 빈 주입 => dataSource 빈에 트랜잭션 적용됨

- <tx:annotation-driven transaction-manager = "txManager" /> => 애너테이션을 사용하여 트랜잭션을 적용하기 위해 txManager 빈을 설정함

 

[ action-service.xml ] pro25/WebContent/WEB-INF/config/action-service.xml

- accService 빈 - AccountService 클래스, accDAO 속성에 accDAO 빈을 주입

 

2. 마이바티스 관련 XML 파일 설정하기

911p

계좌 이체 기능을 SQL문으로 구현한 매퍼 파일을 설정해보자

src.mybatis.mappers 패키지에 account.xml 생성

 

[ account.xml ] pro25/src/mybatis/mappers/account.xml

: 매퍼파일, 2개의 update문으로 두 명의 계좌 잔고를 갱신함

- <update> id="updateBalance1", SQL - 홍길동의 계좌에서 잔고를 5백만원 감액함

- <update> id="updateBalance2" SQL - 김유신의 계좌에서 잔고를 5백만원 증액함

* 조건으로는 계좌번호 accountNO 를 사용함

 

3. 트랜잭션 관련 자바 클래스와 JSP 파일 구현하기

912p

src 패키지에 com.spring.account 패키지 생성 - AccountContoller, AccountDAO, AccountService, AccountVO 생성

WebContent/WEB-INF/views/account 폴더 - result.jsp 생성

 

[ AccountController ] pro25/src/com/spring/account/AccountController.java

- AccountService 속성 선언

- setAccountService() - accService 속성에 accService 빈을 주입하기 위한 setter

- sendMoney() - ModelAndView 객체 mav 생성, accService.sendMoney() 호출 (금액을 이체함), mav에 setViewName() 인자로 "result" 를 지정함 -> viewResolver에 의해 result.jsp로 연결됨

 

[ AccountService ] pro25/src/com/spring/account/AccountService.java

Service 클래스의 메서드들은 단위기능을 수행하므로 메서드에 트랜잭션을 적용하기 위해 Service 클래스에 @Transactional 애너테이션을 적용함

- @Transactional(propagation=Propagation.REQUIRED) -

  propagation : 트랜잭션 전파 규칙,

  REQUIRED : 트랜잭션 필요, 진행 중인 트랜잭션이 있는 경우 해당 트랜잭션 사용 / 트랜잭션이 없으면 새로운 트랜잭션 생성, 디폴트값임

- AccountDAO 속성 선언

- setAccDAO() - accDAO 속성에 accDAO 빈을 주입하기 위한 setter

- sendMoney() - accDAO.updateBalance1(), accDAO.updateBalance2() 호출하여 2개의 SQL문을 실행함

 

[ AccountDAO ] pro25/src/com/spring/account/AccountDAO.java

각 예금좌 계좌를 갱신하는 메서드를 구현함

- SqlSession 속성 선언

- setSqlSession() - SqlSession 속성에 sqlSession 빈을 주입하기 위한 setter

- updateBalance1() - sqlSession.update(쿼리id) : 홍길동 계좌 잔액 5백만원 차감 SQL

- updateBalance2() - sqlSession.update(쿼리id) : 김유신 계좌 잔액 5백만원 증액 SQL

 

=> 

- 트랜잭션 적용x (AccountService의 @Transactional 애너테이션 주석처리)

updateBalance2 SQL문에 구문오류 발생하도록 수정 후 브라우저에서 /account/sendMoney.do 요청 시,

500 에러 발생,

홍길동의 계좌 잔액은 5백만원 감소했으나 김유신의 개좌 잔액은 그대로 1천만원임

 

- 트랜잭션 적용o (주석 해제)

브라우저에서 /account/sendMoney.do 요청 시,

500 에러 발생,

이번에는 트랜잭션이 적용되었으므로 오류가 발생하지 않은 updateBalance1 의 SQL 수행도 롤백되어

홍길동과 김유신의 계좌 잔액은 모두 변동없이 1천만원임