본문 바로가기

프로그래밍 기초/SPRING

[Spring]트랜잭션과 예외 처리

트랜잭션(transaction)이란?

데이터베이스는 다수의 사용자가 동시에 사용하더라도 항상 모순이 없는 정확한 데이터를 유지해야 한다. 그리고 데이터베이스에 장애가 발생하더라도 빠른 시간 내에 원래의 상태로 복구할 수 있어야 한다. 데이터베이스 관리 시스템은 데이터베이스가 항상 정확하고 일관된 상태를 유지할 수 있도록 다양한 기능을 제공하는데, 그 중심에는 트랜잭션이 있다. 트랜잭션이라는 것을 관리함으로써 데이터베이스의 회복과 병행 제어가 가능해져 결과적으로 데이터베이스가 일관된 상태를 유지할 수 있게 되는 것이다.

트랜잭션(transaction)은 하나의 작업을 수행하기 위해 필요한 데이터베이스의 연산들을 모아놓은 것으로, 데이터베이스에서 논리적인 작업의 단위가 된다. 트랜잭션은 장애가 발생했을 때 데이터를 복구하는 작업의 단위도 된다. 일반적으로 데이터베이스 연산은 SQL 문으로 표현되므로 트랜잭션을 작업 수행에 필요한 SQL 문들의 모임으로 이해해도 좋다.

[네이버 지식백과] 트랜잭션의 개념 (데이터베이스 개론, 2013. 6. 30., 김연희)


트랜잭션의 사용

상품을 구매할 수 있는 페이지를 구성한다고 상상하고

다음과 같은 상품관리(shop),고객계좌(client_account),주문 관리(client-order)할테이블 3개를 생성해보자.

 

상품구매트랜잭션은 세가지 연산으로 구성할 수 있다.

 

1.상품관리(shop)-상품의 재고갯수는 줄어든다.

2.고객계좌(client_account)-고객계좌의 잔고는 줄고,

3. 고객계좌의 point는 늘어난다. 

 

만약 트랜잭션을 사용하지 않는다면 한가지 연산과정에서 에러가 났어도 다른 연산은 처리되어 문제가 될 수 있다.

ex)상품의 재고는 줄어들었으나 고객계좌의 잔고는 줄지 않는다.

 

 

Quantum-DB : 필요한 테이블 만들기

DELETE FROM shop;
DELETE FROM client_account; 

CREATE TABLE shop(
	num NUMBER PRIMARY KEY, --상품번호
	name VARCHAR2(30), --상품이름
	price NUMBER, --상품가격
	remainCount NUMBER CHECK(remainCount >= 0) --재고갯수 
);

-- 고객 계좌 테이블
CREATE TABLE client_account(
	id VARCHAR2(30) PRIMARY KEY, -- 고객의 아이디
	money NUMBER CHECK(money >= 0), -- 고객의 잔고 
	point NUMBER
);

-- 주문 테이블
CREATE TABLE client_order(
	num NUMBER PRIMARY KEY, -- 주문번호
	id VARCHAR2(30), -- 주문 고객의 아이디
	code NUMBER, -- 주문한 상품의 번호 
	addr VARCHAR2(50) -- 배송 주소
);

-- 주문 테이블에 사용할 시퀀스 
CREATE SEQUENCE client_order_seq;


-- sample 데이터
INSERT INTO shop (num,name,price,remainCount)
VALUES(1, '사과', '1000', 5);

INSERT INTO shop (num,name,price,remainCount)
VALUES(2, '바나나', '2000', 5);

INSERT INTO shop (num,name,price,remainCount)
VALUES(3, '귤', '3000', 5);

INSERT INTO client_account (id, money, point)
VALUES('superman', 10000, 0);

INSERT INTO client_account (id, money, point)
VALUES('batman', 10000, 0);

 

 


트랜잭션을 위한 환경설정

1.pom.xml에  spring-tx dependency추가

		<!-- 트렌젝션 처리를 위한 라이브러리 -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-tx</artifactId>
		    <version>4.0.0.RELEASE</version>
		</dependency>		

 

servlet context의 namespaces 체크박스 설정해준다.

 

3.servlet context에 transaction설정을 추가한다.

beans에 명시되어있는 bean은 component-scan과 상관없이 spring에서 bean으로 관리된다.

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	<!-- Transaction Manager설정 -->
	<beans:bean id="txManager" 
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<beans:property name="dataSource" ref="dataSource"/>
	</beans:bean>
    
   <!-- 어노테이션으로 트렌젝션 설정이 가능하도록 -->
	<tx:annotation-driven transaction-manager="txManager"/>
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

txManager=new DataSourceTransactionManager()'

txManager.setDataSource(dataSource);

 

 

@Transactional어노테이션을 추가한다.

@Transactional이 있는 클래스를 수행하다가 특정 exception이 발생되면 자동으로 트랜잭션관리되어 롤백된다.

트랜잭션을 붙여놨기 때문에 한가지 때문에 exception 이 발생되어도 다른 요소들도 초기화되어 적용받지 않는다. 

ex)재고의 갯수가 0보다 작아져서 exception이 발생되면 10%적립도 되지않는다.

 

이떄 프로그래머가 임의도 exception을만들어 comstum exception을 발생 시킬 수 있다. (ex.배송 불가능한 지역입니다)

참고 https://dev-jejeb.tistory.com/50

 

ShopServiceImpl

	//상품 구입처리를 하는 비즈니스 로직
	@Transactional
	@Override
	public void buy(HttpServletRequest request, ModelAndView mView) {
		//로그인된 아이디
		String id=(String)request.getSession().getAttribute("id");
		//구입할 상품번호
		int num=Integer.parseInt(request.getParameter("num"));
		//1. 상품의 가격정보 얻어오기
		int price=shopDao.getPrice(num);
		//2. 상품의 가격만큼 계좌 잔액 줄이기
		ShopDto dto=new ShopDto();
		dto.setId(id);
		dto.setPrice(price);
		dto.setNum(num);
		shopDao.minusMoney(dto);
		//3.가격의 10%를 포인트로 적립
		shopDao.plusPoint(dto);
		//4.재고의 갯수를 1 감소시킨다.
		shopDao.minusCount(num);
		//5.배송 요청정보를 추가한다.
		OrderDto dto2=new OrderDto();
		dto2.setId(id);
		dto2.setCode(num);
		dto2.setAddr("강남역 스타벅스");
		orderDao.addOrder(dto2);
	}

 

 


exception처리하기

exceptionController

class DataAccessException{

  private String message:

  public String getMessage(){

  return this.message;

라는내용이 DataAcceessException에 들어있다.

	@ExceptionHandler(DataAccessException.class)
	public ModelAndView dataAccess(DataAccessException dae) {
		ModelAndView mView=new ModelAndView();
		//"exception"이라는 키값으로 예외객체를 담는다.
		mView.addObject("exception",dae);
		//view page설정
		mView.setViewName("error/data_access");
		return mView;
	}

 

/error/data_access.jsp

위에서 받은 exception의 message를 받아왔다.

<div class="container">
	<h1>DB관련 예외발생!</h1>
	<p class="alert alert-danger">${exception.message }</p>
	<a href="${pageContext.request.contextPath}/home.do">인덱스로 가기</a>
</div>