Spring/Boot

[Boot] Transaction

hvoon 2022. 9. 10. 21:08

동작에 오류가 발생하면 처음 상태로 되돌림

sql.sql

drop table transaction1;
drop table transaction2;
drop table transaction3;

create table transaction1(
id varchar2(20),
amount number(10)
);
create table transaction2(
id varchar2(20),
amount number(10)
);
create table transaction3(
id varchar2(20),
amount number(10)
);
 
 

 

Controller

package com.ecl.g13.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

	@RequestMapping("/")
	public String root() {
		return "buy_ticket";
	}
이 안에 추가될 코드 배경 - 줄무늬
}

buy_ticket.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>buy_ticket</title>
</head>
<body>

<p>카드 결제</p>
<form action="butTicketCard">
	고객 아이디: <input type="text" name="id"><br/>
	티켓 구매수: <input type="text" name="amount"><br/>
	에러 발생 여부: <input type="text" name="error" value="0"><br/>
	<input type="submit" value="구매"><br/>
</form>
<hr>
에러 발생 여부에 1을 입력하면 에러가 발생함

</body>
</html>

 

Controller

	@Autowired
	MyService ms;

	@RequestMapping("/buyTicketCard")
	public String buy_ticket_card(
		@RequestParam("id") String id,
		@RequestParam("amount") int amount,
		@RequestParam("error") int error, Model model) {
		// 현재 해야할 일은 전달된 아이디가 티켓을 전달된 구매갯수만큼 
		// 구매한 걸로 데이터베이스 테이블에 insert 하는 것
		int result = ms.buy(id, amount, error);
		// 전달된 아이디, 구매갯수, 에러여부를 서비스단에 전달하여 구매작업을 계속진행
		// 구매작업 성공여부를 리턴받아서 성공이면 buy_ticket_end.jsp로
		// 실패하면 buy_ticket_error.jsp로 이동
		
		model.addAttribute("id",id);
		model.addAttribute("amount",amount);
		model.addAttribute("error",error);
		
		if(result == 1) return "buy_ticket_end";
		else return "buy_ticket_error";
	}

Service

package com.ecl.g13.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ecl.g13.dao.ITransactionDao1;
import com.ecl.g13.dao.ITransactionDao2;

@Service
public class MyService {

	@Autowired
	ITransactionDao1 td1;
	
	@Autowired
	ITransactionDao2 td2;
	
	public int buy(String id, int amount, int error) {
		int result = 0;

		td1.buy(id, amount);
		if(error == 1) {
			int n = 10 / 0; // 전달된 error 값이 1이라면 강제 에러 발생
		}
		
		td2.buy(id, amount);

		if(error == 1) result = 0;
		else result = 1;

		return result;
	}
}

interface Dao

package com.ecl.g13.dao;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ITransactionDao1 {
	public void buy(String id, int amount);
}
package com.ecl.g13.dao;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ITransactionDao2 {
	public void buy(String id, int amount);
}

src\main\resources\mybatis\mapper\TransactionDao1.xml

<?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 = "com.ecl.g13.dao.ITransactionDao1" >
	<insert id="buy">
		insert into transcation1(id, amount) values(#{param1}, #{param2})
	</insert>
</mapper>

src\main\resources\mybatis\mapper\TransactionDao2.xml

<?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 = "com.ecl.g13.dao.ITransactionDao2" >
	<insert id="buy">
		insert into transcation2(id, amount) values(#{param1}, #{param2})
	</insert>
</mapper>

buy_ticket_end.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>buy_ticket_end.jsp</title>
</head>
<body>

<h1>buy_ticket_end</h1>
<h1>티켓의 아래의 정보로 정상 구매 되었습니다.</h1>
<h2>아이디: ${id }</h2>
<h2>수량: ${amount }</h2>
<h2>에러: ${error }</h2>

</body>
</html>

buy_ticket_error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>buy_ticket_error.jsp</title>
</head>
<body>

<h1>buy_ticket_error</h1>
<h1>에러 발생</h1>
<h2>아이디: ${id }</h2>
<h2>수량: ${amount }</h2>
<h2>에러: ${error }</h2>

</body>
</html>

Service

	@Autowired
	PlatformTransactionManager ptm;
	
	@Autowired
	TransactionDefinition td;

	public int buy(String id, int amount, int error) {
		int result = 0;
		// 전달된 아이디와 구매갯수를 transaction1,transaction2 
		// 두 개의 테이블에 insert함
		// 하나 이상의 데이터베이스 작업을 묶어서
		// 하나의 실행단위로 정의된 것을 트랙잭션이라고 함
		// 트랙잭션 하나가 모두 다 실행이 되어 완료되면
		// commit이라는 명령으로 작업을 완료하고
		// 중간에 에러가 발생하면 트랙잭션을 취소하고자 한다면
		// rollback이라는 명령을 취소함
		
		// 트랙잭션의 시작
		TransactionStatus status = ptm.getTransaction(td);
		// 끝은 commit 또는 rollback
		
		try {
			td1.buy(id, amount);
			if(error == 1) {
				int n = 10 / 0;
			}
			td2.buy(id, amount);
			System.out.println("error없이 둘 다 실행됨");
			ptm.commit(status); 
			// 영역 안의 모든 데이터베이스 작업의 실행적용 - 트랙잭션 끝
			result = 1;
		} catch(Exception e) {
			System.out.println("error나서 실행 안됨");
			ptm.rollback(status);
			// 영역 안의 모든 데이터베이스 작업의 취소
			result = 0;
		}
		return result;
	}
 

방법 2

Service

	@Autowired
	TransactionTemplate tt;

	public int buy(String id, int amount, int error) {
		try {
			tt.execute(new TransactionCallbackWithoutResult() {

				@Override
				protected void doInTransactionWithoutResult(TransactionStatus status){
					// 현재 트랙잭션에 포함될 명령들을 이 위치에 기술함
					td1.buy(id, amount);
					if(error == 1) {
						int n = 10 / 0;
					}
					td2.buy(id, amount);
				}
			});
			return 1;
		} catch(Exception e){
			return 2;
		}
	}