본문 바로가기

개발자노트/웹

트랜잭션 예제 - 은행 계좌이체 예제

은행 계좌이체시 잔액이 옮겨지는 것을 이용한 트랜잭션 이해 예제이다.

 

 

1번 은행용 Bank1.java코드

package vo;

public class Bank1 {
	private int bid;
	private String bname;
	private int balance;
	public int getBid() {
		return bid;
	}
	public void setBid(int bid) {
		this.bid = bid;
	}
	public String getBname() {
		return bname;
	}
	public void setBname(String bname) {
		this.bname = bname;
	}
	public int getBalance() {
		return balance;
	}
	public void setBalance(int balance) {
		this.balance = balance;
	}
	@Override
	public String toString() {
		return "Bank1 [bid=" + bid + ", bname=" + bname + ", balance=" + balance + "]";
	}
	
	
}

2번 은행은 위에서 클래스명만 다르니 올리진 않겠다.

 

Bank1DAO.java 코드

현재는 한방향으로 Bank1 → Bank2 로만 송금이 된다는 조건이다.

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import util.JDBCUtil;
import vo.Bank1;

public class Bank1DAO {
	Connection conn;
	PreparedStatement pstmt;
	final String sql_transfer1="UPDATE BANK1 SET BALANCE=BALANCE-? WHERE BID=101";
	final String sql_transfer2="UPDATE BANK2 SET BALANCE=BALANCE+? WHERE BID=222";
	public boolean transfer(int balance) {
		// DAO의 메서드는 일반적으로 vo
		// 강제적으로 자료형,인자의 개수를 고정할수도있음!
		conn=JDBCUtil.connect();
		try {
			conn.setAutoCommit(false);
			// 트랜잭션의 시작을 설정하는 메서드
			// 자동commit을 해제할수있음(MySQL)
			
			// +++하나의 작업단위+++
			pstmt=conn.prepareStatement(sql_transfer1);
			pstmt.setInt(1, balance);
			pstmt.executeUpdate();
			
			pstmt=conn.prepareStatement(sql_transfer2);
			pstmt.setInt(1, balance);
			pstmt.executeUpdate();
			// +++하나의 작업단위+++
			
			System.out.println("수행");
			pstmt=conn.prepareStatement(sql_selectOne);
			ResultSet rs=pstmt.executeQuery();
			rs.next();
			System.out.println(rs.getString("BNAME")+" | "+rs.getInt("BALANCE"));
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			rs.close();
			
			pstmt=conn.prepareStatement(sql_selectOne);
			rs=pstmt.executeQuery();
			rs.next();
			if(rs.getInt("BALANCE")<0) { // 가진금액보다 더많이 계좌이체를 하려고할때
				conn.rollback();
				return false;
			}
			else {
				conn.commit();
			}
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
			return false;
		} finally {
			JDBCUtil.disconnect(pstmt, conn);
		}
		return true;
	}
	
	final String sql_selectOne="SELECT * FROM BANK1 WHERE BID=101";
	public Bank1 selectOne(Bank1 vo) {
		Bank1 data=null;
		conn=JDBCUtil.connect();
		try {
			pstmt=conn.prepareStatement(sql_selectOne);
			ResultSet rs=pstmt.executeQuery();
			if(rs.next()) {
				data=new Bank1();
				data.setBalance(rs.getInt("BALANCE"));
				data.setBid(rs.getInt("BID"));
				data.setBname(rs.getString("BNAME"));
				System.out.println("Bank1DAO: selectOne(): "+data);
				return data;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.disconnect(pstmt, conn);
		}
		return data;
	}
}

 

Bank2DAO.java 코드

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import util.JDBCUtil;
import vo.Bank2;

public class Bank2DAO {
	Connection conn;
	PreparedStatement pstmt;
	
	final String sql_selectOne="SELECT * FROM BANK2 WHERE BID=222";
	public Bank2 selectOne(Bank2 vo) {
		Bank2 data=null;
		conn=JDBCUtil.connect();
		try {
			pstmt=conn.prepareStatement(sql_selectOne);
			ResultSet rs=pstmt.executeQuery();
			if(rs.next()) {
				data=new Bank2();
				data.setBalance(rs.getInt("BALANCE"));
				data.setBid(rs.getInt("BID"));
				data.setBname(rs.getString("BNAME"));
				System.out.println("Bank2DAO: selectOne(): "+data);
				// 어디서 잘못됐는지 확인이 편한 위치의 로그
				// selectOne이 잘 됐으면 V나 C 잘못인거지..
				return data;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.disconnect(pstmt, conn);
		}
		return data;
	}
}

 

위에 DAO 코드들에는 각각의 멤버변수들을 볼 수 있게 selectOne이 포함되어있다.

 

 

. jsp 코드1

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>view</title>
</head>
<body>

신한: ${b1.name} | ${b1.balance}원 <br>
국민: ${b2.name} | ${b2.balance}원

<hr>

<form method="post">
	이체할 금액: <input type="number" value="0" min="0" name="balance">원
	<input type="submit" value="계좌이체">
</form>

</body>
</html>

post 방식으로 form에 입력한 값을 .jsp 2번째 파일로 넘겨주면

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<jsp:useBean id="b1" class="vo.Bank1" />
<jsp:useBean id="b2" class="vo.Bank2" />
<jsp:useBean id="dao1" class="dao.Bank1DAO" />
<jsp:useBean id="dao2" class="dao.Bank2DAO" />
<%						//null은 parseInt 할 수 없으니 유의!
		if(dao1.transfer(Integer.parseInt(request.getParameter("balance")))){
			out.print("<script>alert('성공!');location.href='NEWFILE.JPS'</script>");
		}
		else{
			out.print("<script>alert('실패...');</script>");
		}

	b1=dao1.selectOne(b1);
	b2=dao2.selectOne(b2);
	session.setAttribute("b1", b1); // V에서 EL식으로 출력하기 위해
	session.setAttribute("b2", b2); // JSP scoper 내장객체에 setAttribute 함
%>

성공시 다시 location.href를 통해 1번 jsp 파일로 이동시켜준다.