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;
}
}