계좌 개설 및 입출금, 계좌 이체 프로그램 만드는 예제
** 요구사항 **
사용자는 계좌 개설이 가능하다.
계좌 계설 시
--> 1. 계좌번호, 계좌주명, 비밀번호, 잔액정보가 저장되어야 한다
--> 2. 최초 계좌 개설 시에는 초기 납입액 1000원 이상이 되어야 한다
--> 3. 계좌번호는 유일해야 하고, 시스템에서 자동 발급되도록 관리한다 (primary key, sequence)
잔액 확인 시
--> 계좌번호가 존재해야 하고, 비밀번호가 일치해야 한다.
입금액, 출금액, 계좌 이체액은 모두 0원을 초과해야 한다.
입금 시에는
--> 계좌번호, 비밀번호가 일치해야 한다.
출금 시에는
--> 계좌번호, 비밀번호, 잔액 확인 절차가 필요하다.
계좌 이체 시에는
--> 1. 송금자 및 수금자의 계좌가 존재해야 한다.
--> 2. 이체액은 0원을 초과해야한다.
--> 3. 송금자의 비밀번호가 일치해야 한다.
--> 4. 송금자의 잔액확인이 필요하다.
SQL
ACCOUNT_INST TABLE 생성
CREATE TABLE account_inst (
account_no NUMBER PRIMARY KEY,
name VARCHAR2(100) NOT NULL,
password VARCHAR2(100) NOT NULL,
balance NUMBER DEFAULT 1000
);
account_inst_seq SEQUENCE 생성
: cache기능을 사용하지 않겠다는 옵션을 할당
-- > 비정상 종료 시에도 번호가 이어지도록
CREATE SEQUENCE account_inst_seq NOCACHE;
UML
JAVA JDBC
TestDAO class (main class)
step1. 계좌 개설 단위 테스트
/TestUnit1.java
package test;
import java.sql.SQLException;
import model.AccountDAO;
import model.AccountVO;
import model.CreateAccountException;
// step1. 계좌 개설 단위 테스트
public class TestUnit1 {
public static void main(String[] args) {
try {
AccountDAO dao=new AccountDAO();
//개설할 계좌 정보
AccountVO vo=new AccountVO("아이유","1234",1000);
//AccountVO vo=new AccountVO("장기하","4321",2000);
try{
dao.createAccount(vo);
System.out.println("계좌 개설 성공");
}catch(CreateAccountException se){
System.out.println(se.getMessage());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch(SQLException e){
e.printStackTrace();
}
}
}
step2. 계좌 잔액 조회 테스트
/TestUnit2.java
package test;
import java.sql.SQLException;
import model.AccountDAO;
import model.AccountNotFoundException;
import model.NotMatchedPasswordException;
//step2 계좌 잔액조회 테스트
public class TestUnit2 {
public static void main(String[] args) {
try {
AccountDAO dao = new AccountDAO();
System.out.println("잔액조회:"
+ dao.getAccountBalance("1", "1234"));
}catch (AccountNotFoundException e) {
System.out.println(e.getMessage());
}catch (NotMatchedPasswordException e) {
System.out.println(e.getMessage());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
step3. 입금 테스트
/TestUnit3.java
package test;
import java.sql.SQLException;
import model.AccountDAO;
import model.AccountNotFoundException;
import model.NoMoneyException;
import model.NotMatchedPasswordException;
//step3 입금 테스트
public class TestUnit3 {
public static void main(String[] args) {
try {
AccountDAO dao = new AccountDAO();
dao.deposit("1","12345", 200);
System.out.println("정상입금처리");
}catch (AccountNotFoundException e) {
System.out.println(e.getMessage());
}catch (NoMoneyException e) {
System.out.println(e.getMessage());
}catch (NotMatchedPasswordException e) {
System.out.println(e.getMessage());
}catch (SQLException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
step4. 출금 테스트
/TestUnit4.java
package test;
import java.sql.SQLException;
import model.AccountDAO;
import model.AccountNotFoundException;
import model.NotMatchedPasswordException;
import model.InsufficientBalanceException;
import model.NoMoneyException;
//step4 출금 테스트
public class TestUnit4 {
public static void main(String[] args) {
try {
AccountDAO dao = new AccountDAO();
dao.withdraw("1","1234", 2000);
System.out.println("정상출금처리");
}catch (NoMoneyException e) {
System.out.println(e.getMessage());
}catch (AccountNotFoundException e) {
System.out.println(e.getMessage());
}catch (NotMatchedPasswordException e) {
System.out.println(e.getMessage());
}catch (InsufficientBalanceException e) {
System.out.println(e.getMessage());
}catch (SQLException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
step5. 계좌 이체 테스트
/TestUnit5.java
package test;
import java.sql.SQLException;
import model.AccountDAO;
import model.NotMatchedPasswordException;
import model.AccountNotFoundException;
import model.InsufficientBalanceException;
import model.NoMoneyException;
//step5 계좌이체 테스트
public class TestUnit5 {
public static void main(String[] args) {
try {
AccountDAO dao = new AccountDAO();
dao.transfer("1","1234","2",10000);
System.out.println("이체 완료");
} catch (NoMoneyException e) {
System.out.println(e.getMessage());
} catch (AccountNotFoundException e) {
System.out.println(e.getMessage());
} catch (InsufficientBalanceException e) {
System.out.println(e.getMessage());
} catch (NotMatchedPasswordException e) {
System.out.println(e.getMessage());
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
}
}
Common class
: DB에 접근하는데 필요한 variable들을 모아놓은 클래스
/DbInfo.java
package common;
public interface DbInfo {
String DRIVER_NAME="oracle.jdbc.driver.OracleDriver";
String URL="jdbc:oracle:thin:@127.0.0.1:1521:xe";
String USER="scott";
String PASS="tiger";
}
DAO ( Data Access Object ) class
: 데이베이스 연동 로직을 정의한 객체
하나의 Connection 내에서 여러 개의 기능을 넣을 수 있다 !
/AccountDAO.java
package model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import common.DbInfo;
public class AccountDAO {
public AccountDAO() throws ClassNotFoundException {
Class.forName(DbInfo.DRIVER_NAME);
}
private Connection getConnection() throws SQLException {
return DriverManager.getConnection(DbInfo.URL, DbInfo.USER, DbInfo.PASS);
}
public void closeAll(ResultSet rs, PreparedStatement pstmt, Connection con) throws SQLException {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (con != null)
con.close();
}
public void closeAll(PreparedStatement pstmt, Connection con) throws SQLException {
closeAll(null, pstmt, con);
}
/**
* 계좌를 개설하는 메서드 계좌번호는 시퀀스를 이용해 자동 생성해 입력하고 계좌주명과 비밀번호,초기 납입금을 데이터베이스에 입력한다
* 초기 납입금이 1000원 미만이면 개설하지 않는다.
*
* @param vo
* @throws CreateAccountException
* @throws SQLException
*/
public void createAccount(AccountVO vo) throws CreateAccountException, SQLException {
if (vo.getBalance() < 1000)
throw new CreateAccountException("초기 납입액은 1000원 이상이어야 합니다!");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
StringBuilder sql = new StringBuilder();
sql.append("insert into account_inst(account_no,name,password,balance)");
sql.append(" values(account_inst_seq.nextval,?,?,?)");
pstmt = con.prepareStatement(sql.toString());
pstmt.setString(1, vo.getName());
pstmt.setString(2, vo.getPassword());
pstmt.setInt(3, vo.getBalance());
pstmt.executeUpdate();
} finally {
closeAll(rs, pstmt, con);
}
}
/**
* 계좌번호와 비밀번호를 입력받아 잔액을 반환하는 메서드 계좌번호가 존재하지 않거나 계좌번호에 따른 비밀번호가 일치하지 않으면 예외를
* 발생시킨다.
*
* @param accountNo
* @param password
* @return
* @throws SQLException
* @throws AccountNotFoundException
* @throws NotMatchedPasswordException
*
*/
public int getAccountBalance(String accountNo, String password)
throws SQLException, NotMatchedPasswordException, AccountNotFoundException {
int balance = -1;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
String sql = "select password,balance from account_inst where account_no=?";
pstmt = con.prepareStatement(sql);
pstmt.setString(1, accountNo);
rs = pstmt.executeQuery();
if (rs.next()) {
if (password.equals(rs.getString(1))) { // 비밀번호가 일치하면
balance = rs.getInt(2);
} else { // 비밀번호가 일치하지 않으면
throw new NotMatchedPasswordException("비밀번호가 일치하지 않습니다!");
}
} else {// 계좌가 존재하지 않으면
throw new AccountNotFoundException("계좌가 존재하지 않습니다!");
}
} finally {
closeAll(rs, pstmt, con);
}
return balance;
}
/**
* 입금처리 메서드
*
* @param accountNo
* @param money
* @throws NoMoneyException
* @throws SQLException
* @throws AccountNotFoundException
* @throws NotMatchedPasswordException
*/
public void deposit(String accountNo, String password, int money)
throws NoMoneyException, SQLException, AccountNotFoundException, NotMatchedPasswordException {
if (money <= 0)
throw new NoMoneyException("입금액은 0원을 초과해야 합니다!");
getAccountBalance(accountNo, password);// 계좌 존재유무와 비밀번호 확인 예외처리를 위해 호출한다
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
String sql = "update account_inst set balance=balance+? where account_no=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, accountNo);
pstmt.executeUpdate();
} finally {
closeAll(rs, pstmt, con);
}
}
/**
* 출금 메서드 money는 0 초과 accountNo 존재하고 password 일치해야 한다 balance 보다 money(출금액) 은
* 같거나 작아야 한다.
*
* @param accountNo
* @param password
* @param money
* @throws NoMoneyException
* @throws AccountNotFoundException
* @throws SQLException
* @throws InsufficientBalanceException
* @throws NotMatchedPasswordException
*/
public void withdraw(String accountNo, String password, int money) throws SQLException, NoMoneyException,
InsufficientBalanceException, NotMatchedPasswordException, AccountNotFoundException {
if (money < 1)
throw new NoMoneyException("출금액은 0원을 초과해야 합니다");
int balance = getAccountBalance(accountNo, password);
if (balance < money)
throw new InsufficientBalanceException("잔액부족하여 출금할 수 없습니다");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
String sql = "update account_inst set balance=balance-? where account_no=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, accountNo);
pstmt.executeUpdate();
} finally {
closeAll(rs, pstmt, con);
}
}
/**
* 계좌번호에 해당하는 계좌가 존재하는 지를 조회하는 메서드 계좌가 존재하면 true , 존재하지 않으면 false를 반환한다
*
* @param accountNo
* @return
* @throws SQLException
*/
public boolean existAccount(String accountNo) throws SQLException {
boolean flag = false;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
String sql = "select count(*) from account_inst where account_no=?";
pstmt = con.prepareStatement(sql);
pstmt.setString(1, accountNo);
rs = pstmt.executeQuery();
if (rs.next() && rs.getInt(1) > 0)
flag = true;
} finally {
closeAll(rs, pstmt, con);
}
return flag;
}
/**
* 이체시 입금받을 메서드 ( 비밀번호 확인 과정이 없음 )
* @param accountNo
* @param money
* @throws NoMoneyException
* @throws SQLException
* @throws AccountNotFoundException
* @throws NotMatchedPasswordException
*/
public void transferDeposit(String accountNo, int money)
throws NoMoneyException, SQLException, AccountNotFoundException, NotMatchedPasswordException {
if (money <= 0)
throw new NoMoneyException("이체액은 0원을 초과해야 합니다!");
if (!existAccount(accountNo))
throw new AccountNotFoundException("이체받을 계좌가 존재하지 않습니다");
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = getConnection();
String sql = "update account_inst set balance=balance+? where account_no=?";
pstmt = con.prepareStatement(sql);
pstmt.setInt(1, money);
pstmt.setString(2, accountNo);
pstmt.executeUpdate();
} finally {
closeAll(rs, pstmt, con);
}
}
/**
* 계좌이체 accountNo 에 해당하는 계좌에서 출금하여 otherAccountNo에 해당하는 계좌에 입금하는 메서드 예외상황
1.transferMoney가 0 이하일 경우 NoMoneyException
2.accountNo 또는 otherAccountNo가
존재하지 않을 경우 AccountNotFoundException
3.accountNo 즉 출금계좌 password가 다를 경우
NotMatchedPasswordException 4. accountNo 즉 출금계좌의 balance(잔액) 이
transferMoney(이체액) 보다 작을 경우 InsufficientBalanceException
* @param accountNo
* @param password
* @param otherAccountNo
* @param transferMoney
* @throws NoMoneyException
* @throws SQLException
* @throws AccountNotFoundException
* @throws InsufficientBalanceException
* @throws NotMatchedPasswordException
*/
public void transfer(String accountNo, String password, String otherAccountNo, int transferMoney)
throws NoMoneyException, SQLException, AccountNotFoundException, InsufficientBalanceException,
NotMatchedPasswordException {
if (transferMoney <= 0)
throw new NoMoneyException("이체액은 0원을 초과해야 합니다!");
withdraw(accountNo, password, transferMoney);
transferDeposit(otherAccountNo, transferMoney);
}
}
Exception class
: 예외상황 발생 시 대안 흐름을 생성하기 위하여 사용
1. 계좌가 없을 때 발생하는 예외
/AccountNotFoundException.java
package model;
public class AccountNotFoundException extends Exception {
private static final long serialVersionUID = -2964134848203606702L;
public AccountNotFoundException(String message) {
super(message);
}
}
2. 계좌 생성 시 발생하는 예외
/CreateAccountException.java
package model;
public class CreateAccountException extends Exception{
private static final long serialVersionUID = 491332526990042396L;
public CreateAccountException(String message){
super(message);
}
}
3. 잔고액에서 출금,송금액을 빼면 -가 되는 경우에 발생하는 예외
/InsufficientBalanceException.java
package model;
public class InsufficientBalanceException extends Exception {
private static final long serialVersionUID = 2046794047983657245L;
public InsufficientBalanceException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
4. 입금/출금/송금액이 0 이하인 경우에 발생하는 예외
/NoMoneyException.java
package model;
public class NoMoneyException extends Exception {
private static final long serialVersionUID = -4236638528117552246L;
public NoMoneyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
5. 비밀번호가 일치하지 않을 때 발생하는 예외
/NotMatchedPasswordException.java
package model;
public class NotMatchedPasswordException extends Exception{
private static final long serialVersionUID = 5628642488426527596L;
public NotMatchedPasswordException(String message){
super(message);
}
}
VO ( Value Object ) class
: 정보를 저장하고 (계층 간/ 원격으로)전송하기 위한 class
/AccountVO.java
package model;
public class AccountVO {
private String accountNo;
private String name;
private String password;
private int balance;
public AccountVO() {
super();
}
public AccountVO(String name, String password, int balance) {
super();
this.name = name;
this.password = password;
this.balance = balance;
}
public AccountVO(String accountNo, String name, String password, int balance) {
super();
this.accountNo = accountNo;
this.name = name;
this.password = password;
this.balance = balance;
}
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "AccountVO [accountNo=" + accountNo + ", name=" + name
+ ", password=" + password + ", balance=" + balance + "]";
}
}