
다음과 같이 프로젝트를 생성합니다.
이전 프로젝트(TransactionTemplate을 활용한 트랜잭션 관리)의 구성을 그대로 유지한채 파라미터 부분만 수정해봅니다.
서비스를 생성합니다. 다음과 같이 com.edu.springboot.jdbc.ITicketService.java 파일에 코드를 추가합니다.
package com.edu.springboot.jdbc;
import org.apache.ibatis.annotations.Mapper;
/* 구매한 티켓과 금액에 대한 insert 처리를 위한 추상메서드 정의 */
@Mapper
public interface ITicketService {
// 매개변수는 커맨드 객체로 처리하기 위해 각 DTO 객체를 사용
public int ticketInsert(TicketDTO ticketDTO);
public int payInsert(PayDTO payDTO);
public int memberRegist(TicketDTO ticketDTO);
}
매퍼를 생성합니다. 다음과 같이 src/main/resources/mybatis/mapper/TicketPayDAO.xml 파일에 코드를 추가합니다.
<insert id="memberRegist"
parameterType="com.edu.springboot.jdbc.TicketDTO">
INSERT INTO member (id, pass, name) VALUES (#{userid}, '^^', '티켓구매')
</insert>
클래스를 생성합니다. 다음과 같이 com.edu.springboot.jdbc.AddMember.java 파일을 작성합니다.
package com.edu.springboot.jdbc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
/* 스프링 컨테이너 시작시 자동으로 기본패키지를 스캔(Scan)한 후 빈을 생성한다. */
@Service
public class AddMember {
// DB 작업을 위한 자동주입
@Autowired
ITicketService dao;
// 트랜젝션 처리를 위한 자동주입
@Autowired
TransactionTemplate transactionTemplate;
/* 전파속성
* REQUIRED : 기존 트랜젝션에 의존한다. 즉 포함된 메서드나 포함시킨 메서드 어느쪽이든 오류가 발생하면 모든 작업이 롤백된다. 즉 모든 메서드로 전파된다.
* REQUIRES_NEW : 각각의 트랜젝션을 처리한다. 즉 포함시킨 메서드에서 오류가 발생되더라도 포함된 메서드에서는 정상 처리된다. 포함된 메서드로 전파되지 않는다. */
// @Transactional(propagation = Propagation.REQUIRED)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void memberInsert(TicketDTO ticketDTO, String errFlag) {
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 체크박스 2를 선택하면 여기서 의도적 에러 발생됨
if (errFlag != null && errFlag.equals("2")) {
int money = Integer.parseInt("200원");
}
// 구매한 사람의 내역을 member 테이블에 추가
int result3 = dao.memberRegist(ticketDTO);
if (result3 == 1) {
System.out.println("member 테이블 입력성공");
}
}
});
}
catch (Exception e) {
System.out.println("member 테이블 롤백");
}
}
}
컨트롤러를 생성합니다. 다음과 같이 com.edu.springboot.MainController.java 파일에 코드를 추가합니다.
package com.edu.springboot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.edu.springboot.jdbc.AddMember;
import com.edu.springboot.jdbc.ITicketService;
import com.edu.springboot.jdbc.PayDTO;
import com.edu.springboot.jdbc.TicketDTO;
import jakarta.servlet.http.HttpServletRequest;
import oracle.jdbc.internal.OracleConnection.TransactionState;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MainController {
@Autowired
ITicketService dao;
@Autowired
TransactionTemplate transactionTemplate;
/* 추가작업 클래스 : 회원테이블에 구매한 사람의 이력을 입력
* 정의시 @Service 어노테이션을 부착한 상태이므로 자동 주입 가능함 */
@Autowired
AddMember addMember;
@RequestMapping("/")
public String home() {
return "home";
}
@GetMapping("/buyTicket.do")
public String buy1() {
return "buy";
}
@PostMapping("/buyTicket.do")
public String buy2(TicketDTO ticketDTO, PayDTO payDTO, HttpServletRequest req, Model model) {
String viewPath = "success";
try {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 1. 회원정보입력 (추가작업, 클래스 외부에서 처리)
String errFlag = req.getParameter("err_flag");
addMember.memberInsert(ticketDTO, errFlag);
// 2. 구매금액입력 (DB처리1)
payDTO.setAmount(ticketDTO.getT_count() * 10000);
int result1 = dao.payInsert(payDTO);
if (result1 == 1) System.out.println("transaction_pay 입력성공");
// 3. 비지니스로직 처리 (의도적 에러 발생 지점)
if (errFlag != null) {
int money = Integer.parseInt("100원");
}
// 4. 티켓 매수 입력 처리 (DB처리2)
int result2 = dao.ticketInsert(ticketDTO);
if (result2 == 1) System.out.println("transaction_ticket 입력성공");
model.addAttribute("ticketDTO", ticketDTO);
model.addAttribute("payDTO", payDTO);
}
});
}
catch (Exception e) {
System.out.println("transaction 테이블 롤백");
viewPath = "error";
}
return viewPath;
}
}
뷰를 생성합니다. 다음과 같이 webapp/WEB-INF/views/buy.jsp 파일을 수정합니다.
<tr>
<th>에러발생</th>
<td>
<input type="checkbox" name="err_flag" value="1" />
티켓구매에서 예외발생
<input type="checkbox" name="err_flag" value="2" />
회원입력에서 예외발생
</td>
</tr>
이제 트랜젝션 전파 속성을 확인할 수 있습니다.
다음과 같이 실행됩니다.

아이디와 수량을 입력한 후 체크를 하지 않고 '전송하기'를 클릭하면 티켓 구매에 성공합니다.

콘솔에는 다음과 같이 출력됩니다. 모든 테이블이 입력됩니다.
member 테이블 입력성공
transaction_pay 입력성공
transaction_ticket 입력성공
이번에는 아이디와 수량을 입력한 후 '티켓구매에서 예외발생'을 체크하고 '전송하기'를 클릭합니다.

티켓구매 작업 수행 중 오류가 발생하여 티켓 구매에 실패합니다.

콘솔에는 다음과 같이 출력됩니다. 모든 테이블이 입력되지 않습니다.
member 테이블 입력성공
transaction_pay 입력성공
transaction 테이블 롤백
마지막으로 아이디와 수량을 입력한 후 '회원입력에서 예외발생'을 체크하고 '전송하기'를 클릭합니다.

회원입력 작업 수행 중 오류가 발생하여 티켓 구매에 실패합니다.

콘솔에는 다음과 같이 출력됩니다. 모든 테이블이 입력되지 않습니다.
member 테이블 롤백
transaction_pay 입력성공
transaction 테이블 롤백
다음과 같이 실행됩니다.

아이디와 수량을 입력한 후 체크를 하지 않고 '전송하기'를 클릭하면 티켓 구매에 성공합니다.

콘솔에는 다음과 같이 출력됩니다. 모든 테이블이 입력됩니다.
member 테이블 입력성공
transaction_pay 입력성공
transaction_ticket 입력성공
이번에는 아이디와 수량을 입력한 후 '티켓구매에서 예외발생'을 체크하고 '전송하기'를 클릭합니다.

티켓구매 작업 수행 중 오류가 발생하여 티켓 구매에 실패합니다.

콘솔에는 다음과 같이 출력됩니다. 회원 테이블이 입력됩니다.
member 테이블 입력성공
transaction_pay 입력성공
transaction 테이블 롤백
마지막으로 아이디와 수량을 입력한 후 '회원입력에서 예외발생'을 체크하고 '전송하기'를 클릭합니다.

회원입력 작업 수행 중 오류가 발생하여 티켓 구매에 실패합니다.

콘솔에는 다음과 같이 출력됩니다. 모든 테이블이 입력되지 않습니다.
member 테이블 롤백
transaction 테이블 롤백