TRANSACTION
더 이상 나눌 수 없는 작업단위, 데이터베이스의 상태를 변경시키기 위해 수행하는 업무단위입니다.
TCL ( Transaction Control Language )
commit과 rollback을 이용하여 데이터 작업을 할 때 문제가 발생하면 rollback을 이용해서 데이터의 변경이 없게 하는 것이고 이상이 없이 잘 되었다면 commit을 하여 데이터를 변경하는 명령어입니다.
예를 들어,
스마트폰으로 은행업무를 한다고 가정해 봅시다. 돈을 1억 원을 보내고 있는데 갑자기 서버가 이상해져서 돈이 보내졌다고 자신의 데이터는 처리가 되었는데, 서버가 이상해서 상대방은 받지 못했다면 굉장히 문제가 커지겠죠? 그래서 이러한 상황을 대비하여 모든 작업이 끝나지 않고 문제가 생기게 되면 rollback을 해야 합니다.
역시 예시를 들어야 이해가 빠를 것 같습니다.
예시
카드 발급 시 작업 단위를 나눠보면 이렇습니다.
(카드 등록 + 포인트 등록) = 카드발급 Transaction
카드 등록 시 문제가 생기면 카드 발급 작업단위는 모두가 취소되어야 하고,
포인트 등록 시 문제가 생겨도 카드발급 작업단위는 모두 취소가 되어야 합니다.
CREATE TABLE card(
id VARCHAR2(100) PRIMARY KEY,
name VARCHAR2(100) NOT NULL
)
CREATE TABLE point(
id VARCHAR2(100) PRIMARY KEY,
point_type VARCHAR2(100) NOT NULL,
point NUMBER NOT NULL
)
STEP 1. 이상현상 발생!
import model.CardDAO;
public class TestTransaction1 {
public static void main(String[] args) {
/*
트랜잭션 처리가 되지 않을 경우 발생할 수 있는 문제를 확인
카드 발급 트랜잭션 => 더 이상 나눌 수 없는 작업 단위 commit , rollback
(카드 등록 + 포인트 등록) = 카드발급 트랜잭션
별도의 트랜잭션 처리가 없으면 카드 발급은 되고 포인트 발급은 되지 않는 이상 현상이
발생 => step2 에서 트랜잭션 처리
*/
try {
CardDAO dao=new CardDAO();
//dao.register("spring","손흥민","cgv",100);
//고의로 문제를 발생시켜본다 point type 을 null 로 전송해서 not null 제약조건에 위배
dao.register("servlet","이강인",null,100);
System.out.println("카드와 포인트 발급 완료");
}catch (Exception e) {
e.printStackTrace();
}
}
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import common.DbInfo;
public class CardDAO {
public void closeAll(PreparedStatement pstmt,Connection con) throws SQLException {
if(pstmt!=null)
pstmt.close();
if(con!=null)
con.close();
}
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(DbInfo.URL, "mango", "apple");
}
/*
* 카드 테이블과 포인트 테이블에 각각 insert
*/
public void register(String id, String name, String pointType, int point) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
try {
con=getConnection();
String cardSql="INSERT INTO card(id,name) VALUES(?,?)";
pstmt=con.prepareStatement(cardSql);
pstmt.setString(1, id);
pstmt.setString(2, name);
int cardResult=pstmt.executeUpdate();
System.out.println("card insert result:"+cardResult);
pstmt.close();
String pointSql="INSERT INTO point(id,point_type,point) VALUES(?,?,?)";
pstmt=con.prepareStatement(pointSql);
pstmt.setString(1, id);
pstmt.setString(2, pointType);
pstmt.setInt(3, point);
int pointResult=pstmt.executeUpdate();
System.out.println("point insert result:"+pointResult);
}finally {
closeAll(pstmt, con);
}
}
rollback과 commit 없이 동작을 시키는 문장을 만들었습니다.
이렇게 되면 어떻게 될까요?
register 메소드에 있는 문장이 한 줄 한줄 실행되면서 첫 번째 카드를 만드는 문장은 아주 완벽하게 실행돼서 card 테이블에 저장될 것입니다.
그다음 point 테이블에 들어가는 문장에서 null 값 때문에 에러가 발생하여 point 테이블에는 데이터가 들어가지 않을 것입니다.
그렇다면... 카드는 만들어졌는데 포인트가 들어가지 않는 문제가 발생할 것이고, 문제가 발생하면 고객들의 항의가 아주 폭주할 겁니다.
따라서 이것을 해결하기 위해 Transaction 명령어가 있는 것입니다. 보러 가보죠.
Step 2. 문제 해결 (commit, rollback)
public class TestTransaction2 {
public static void main(String[] args) {
/*
step2 : 트랜잭션 처리
카드 발급 트랜잭션은 카드 등록과 포인트 등록으로 구성되어 있으므로
두 세부 업무가 모두 완벽하게 수행될 때만 실제 db에 반영 ( COMMIT )
세부 업무에서 문제 발생시 모든 작업을 취소 ( ROLLBACK ) 되게 처리
*/
try {
CardDAO dao=new CardDAO();
//정상 수행 테스트
dao.registerVer2("spring","손흥민","cgv",100);
//고의로 문제를 발생시켜본다 point type 을 null 로 전송해서 not null 제약조건에 위배
//dao.registerVer2("rest","황희찬",null,100);
System.out.println("카드와 포인트 발급 완료");
}catch (Exception e) {
e.printStackTrace();
}
}
}
public void registerVer2(String id, String name, String pointType, int point) throws SQLException {
Connection con=null;
PreparedStatement pstmt=null;
try {
con=getConnection();
// 수동 커밋모드로 설정 ( jdbc 기본은 auto commit )
con.setAutoCommit(false);
String cardSql="INSERT INTO card(id,name) VALUES(?,?)";
pstmt=con.prepareStatement(cardSql);
pstmt.setString(1, id);
pstmt.setString(2, name);
int cardResult=pstmt.executeUpdate();
System.out.println("card insert result:"+cardResult);
pstmt.close();
String pointSql="INSERT INTO point(id,point_type,point) VALUES(?,?,?)";
pstmt=con.prepareStatement(pointSql);
pstmt.setString(1, id);
pstmt.setString(2, pointType);
pstmt.setInt(3, point);
int pointResult=pstmt.executeUpdate();
System.out.println("point insert result:"+pointResult);
con.commit();
System.out.println("모든 작업이 정상 수행되어 commit");
}catch(Exception e) {
con.rollback();
System.out.println("작업 진행 중 문제발생하여 rollback");
//만약 사용하는 측으로 예외를 전파해야 한다면
throw e;
}finally {
closeAll(pstmt, con);
}
}
문장에 con.setAutoCommit(false); 보이시나요. 저 문장이 바로 sql 실행이 끝나고 오토커밋이 되지 않게 막아주는 역할을 하면서 card 테이블에 데이터를 넣지 않고 기다리게 합니다.
그 다음 포인트 테이블에서 데이터 추가할 때 문제가 생기면 저장해 놓았던 카드 테이블에 들어갈 데이터를 커밋하지 않고 이전 상태로 rollback 하는 방식입니다. (에러 발생 시 catch에서 rollback 실시합니다.)
소감
특히나 돈 관련에서는 조심해야하기 때문에 이러한 transaction 문장을 잘 이용해서 어디서부터 어디까지 끊어야하는지 잘 확인해야하고, 정교하게 데이터베이스를 짜야겠습니다. 실제 돈이 움직이지 않고 데이터가 움직이는 것이기 때문에 주의 깊게 생각해야겠습니다.
'코딩 개발 > Java' 카테고리의 다른 글
Java - Servlet 연동 (HTML <form>, <a> tag) (4) | 2023.05.19 |
---|---|
Servlet 생명 주기 (Servlet Life Cycle) (0) | 2023.05.18 |
Java - DB연결을 위한 Cloud Service (feat. AWS EC2, ORACLE) (4) | 2023.04.30 |
Java - Network 응용 심화편(장코드 주의) (0) | 2023.04.26 |
Java - Network (Socket 통신) (2) | 2023.04.24 |