본문 바로가기
Spring

Spring #8 : 스프링 트랜잭션(230120)

by haheehee 2023. 1. 20.

트랜잭션(Transaction)

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

- All 또는 Nothing 방식으로 작업 처리 -> 작업의 일관성 유지

- 웹 애플리케이션에선 Service 클래스의 각 메서드가 애플리케이션의 단위 기능을 수행

 

웹 애플리케이션에서 묶어서 처리하는 단위 기능 예시

- 게시판 글 조회 시 해당 글을 조회하는 기능/ 조회 수를 갱신하는 기능

- 쇼핑몰에서 상품 주문 시 주문 상품을 테이블에 등록하는 기능/ 주문자의 포인트를 갱신하는 기능

- 은행에서 송금 시 송금자의 잔고를 갱신하는 기능/ 수신자의 잔고를 갱신하는 기능

 

스프링의 트랜잭션 속성

property function
propagation 트랜잭션 전파 규칙 설정
isolation 트랜잭션 격리 레벨 설정
readOnly 읽기 전용 여부 설정
rollbackFor 트랜젝션을 롤백(rollback)할 예외 타입 설정
norollbackFor 트랜잭션을 콜백하지 않을 예외 타입 설정
timeout 트랜잭션 타입아웃 시간 설정

 

propagation속성이 갖는 값

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

 

isolation속성이 갖는 값

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

 


스프링 트랜잭션 기능 실습 #1


데이터베이스 명 새로 만들고, 테이블(cust_account) 생성. 

USE customer;

CREATE TABLE cust_account(
	accountNo VARCHAR(20) PRIMARY KEY,
	custName VARCHAR(50),
	balance DOUBLE(20,4)
);

INSERT INTO cust_account(accountNo,custName,balance)
VALUES('70-490-930','홍길동',10000000);
INSERT INTO cust_account(accountNo,custName,balance)
VALUES('70-490-911','김유신',10000000);

COMMIT;

SELECT * FROM cust_account;

accountNo : 계좌 번호

custName : 예금자

balance : 계좌잔고

insert문실행 후 반드시 commit하기.


<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/views/account/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<bean id="accController"
		class="com.spring.account.AccountController">
		<property name="methodNameResolver">
			<ref local="MethodResolver" />
		</property>
		<property name="accService" ref="accService" />
	</bean>
	<bean id="MethodResolver"
		class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
		<property name="mappings">
			<props>
				<prop key="/account/sendMoney.do">sendMoney</prop>
			</props>
		</property>
	</bean>
	<bean id="UrlMapping"
		class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
			<props>
				<prop key="/account/*.do">accController</prop>
			</props>
		</property>
	</bean>
</beans>

action-servlet.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<bean id="propertyPlaceholderConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>/WEB-INF/config/jdbc.properties</value>
		</property>
	</bean>
	<bean id="dataSource"
		class="org.apache.ibatis.datasource.pooled.PooledDataSource">
		<property name="driver" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	<bean id="sqlSessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!--<property name="configLocation" value="classpath:mybatis/model/modelConfig.xml" />-->
		<property name="mapperLocations" value="classpath:mybatis/mappers/*.xml" />
	</bean>
	<bean id="sqlSession"
		class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
	</bean>
	<bean id="accDAO"
		class="com.spring.account.AccountDAO">
		<property name="sqlSession" ref="sqlSession" />
	</bean>
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<tx:annotation-driven transaction-manager="txManager" />
</beans>

action-mybatis.xml

 

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

	<bean id="accService"
		class="com.spring.account.AccountService">
		<property name="accDAO" ref="accDAO" />
	</bean>
</beans>

action-service.xml

 

jdbc.driverClassName=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mariadb://localhost:3306/customer
jdbc.username=root
jdbc.password=1234

jdbc.properties


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="mapper.account">
	<update id="updateBalance1">
		<![CDATA[ 
			update cust_account
			set balance=balance-5000000
			where accountNo = '70-490-930'
		]]>
	</update>
	<update id="updateBalance2">
		<![CDATA[ 
			update cust_account
			set balance=balance+5000000
			where accountNo = '70-490-911'
		]]>
	</update>	
</mapper>

account.xml


package com.spring.account;

public class AccountVO {
	private String accountNo;
	private String custName;
	private int balance;
	
	public AccountVO() {
		//System.out.println("MemberVO 생성자 호출(객체 생성)");
	}
	public AccountVO(String accountNo, String custName, int balance) {
		this.accountNo = accountNo;
		this.custName = custName;
		this.balance = balance;
	}

	public String getAccountNo() {
		return accountNo;
	}
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	public String getCustName() {
		return custName;
	}
	public void setCustName(String custName) {
		this.custName = custName;
	}
	public int getBalance() {
		return balance;
	}
	public void setBalance(int balance) {
		this.balance = balance;
	}
}

AccountVO.java

 

package com.spring.account;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

public class AccountController extends MultiActionController {
	private AccountService accService;

	public void setAccService(AccountService accService) {
		this.accService = accService;
	}
	
	public ModelAndView sendMoney(HttpServletRequest request, HttpServletResponse response) throws Exception {
		ModelAndView mav = new ModelAndView();
		accService.sendMoney();
		mav.setViewName("result");
		return mav;
	}
}

AccountController.java

 

package com.spring.account;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(propagation=Propagation.REQUIRED)
public class AccountService {
	private AccountDAO accDAO;

	public void setAccDAO(AccountDAO accDAO) {
		this.accDAO = accDAO;
	}
	
	public void sendMoney() throws Exception {
		accDAO.updateBalance1();
		accDAO.updateBalance2();
	}
}

AccountService.java

 

package com.spring.account;

import org.apache.ibatis.session.SqlSession;
import org.springframework.dao.DataAccessException;

public class AccountDAO {
	private SqlSession sqlSession;
	
	public void setSqlSession(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}
	
	public void updateBalance1() throws DataAccessException {
		sqlSession.update("mapper.account.updateBalance1");
	}
	
	public void updateBalance2() throws DataAccessException {
		sqlSession.update("mapper.account.updateBalance2");
	}
}

AccountDAO.java

 


WEB-INF폴더 안에 views폴더-account폴더 생성

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<% request.setCharacterEncoding("utf-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>결과창</title>
</head>
<body>
	<h1>송금이 완료되었습니다. </h1>
</body>
</html>

result.jsp

 


-- 결과 --

↓↓↓↓↓

 

 

 

 

댓글