다음과 같은 구조를 만든다.
main
main - BankApp
import dao.BankDAO; import model.Account; import java.util.List; import java.util.Scanner; public class BankApp { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 디비한테 데이터를 요청할 때는 /뒤에 식별자를 쓴다. // http://bank.com/account GET 계좌 전체 줘 // http://bank.com/account/10 GET 10번(프라이머리키) 계좌 줘 // http://bank.com/account POST 계좌를 만드는 것! // -> 이 정보만으로는 포스트 못 함 // http://bank.com/account/1 DELETE 1번(프라이머리키) 계좌 삭제해 줘! // http://bank.com/account/1 PUT // -> 이 정보만으로는 풋 못 함 // 겟 딜리트 요청은 http에 바디가 없다. // 포스트, 풋 요청은 http에 바디가 없다. // 풋, 딜리트, 업데이터는 write요청이다. -> 응답에 바디가 없다. 오케이만 해주면 됨. System.out.println("http 메서드를 입력하세요"); String method = sc.nextLine(); System.out.println("식별자를 입력하세요"); String action = sc.nextLine(); String body = ""; BankDAO bankDAO = new BankDAO(); // GET: 조회 if (method.equalsIgnoreCase("GET")) { if (action.equalsIgnoreCase("/account")) { List<Account> accountList = bankDAO.selectAll(); System.out.println(accountList); } else if (action.contains("/account/")) { String[] actionSpl = action.split("/"); String actionNumStr = actionSpl[2]; int actionNum = Integer.parseInt(actionNumStr); Account account = bankDAO.selectByNumber(actionNum); System.out.println("account: " + account); } } // POST: 입력 else if (method.equalsIgnoreCase("POST")) { if (action.equalsIgnoreCase("/account")) { System.out.println("body 데이터를 입력하세요."); body = sc.nextLine(); System.out.println("/account 입력됨"); String[] step1 = body.split("&"); String password = step1[0].split("=")[1]; String balanceStr = step1[1].split("=")[1]; int balance = Integer.parseInt(balanceStr); int account = bankDAO.insert(password, balance); // 왜 int로 해야 되는거지? } } // PUT: 수정 else if (method.equalsIgnoreCase("PUT")) { if (action.equalsIgnoreCase("/account")) { System.out.println("body 데이터를 입력하세요"); // balance=70000000&number=3 body = sc.nextLine(); String[] step1 = body.split("&"); String balanceStr = step1[0].split("=")[1]; int balance = Integer.parseInt(balanceStr); // number 찾기 String numberStr = step1[1].split("=")[1]; int number = Integer.parseInt(numberStr); int account = bankDAO.updateByNumber(balance, number); } } // DELETE: 삭제 else if (method.equalsIgnoreCase("DELETE")) { if (action.equalsIgnoreCase("/account")) { System.out.println("삭제하고 싶은 번호를 입력하세요."); int deleteNum = sc.nextInt(); int account = bankDAO.deleteByNumber(deleteNum); } } } }
main - dao-BankDAO
package dao; // 데이터 베이스에 접근하기 위한 데이터 액세스 오브젝트를 만든다. 전 세계 공통!! import db.DBConnection; import lombok.AllArgsConstructor; import model.Account; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * Controller: 안내원, 라우터 * DAO: Data Access Object, 데이터베이스와 상호작용 하는 책임만 가지고 있다. * SRP: 단일 책임의 원칙 */ public class BankDAO { public int deleteByNumber(int number) { /** * 1. 디비 소켓 가져오기 * 2. 버퍼에 쿼리 담기 * 3. 쿼리 전송 * 4. 결과 리턴 */ Connection conn = DBConnection.getInstance(); // getInstance()는 스테이틱 메서드이기 때문에 이렇게 부를 수 있어. try { String sql = "delete from account_tb where number = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); // delete pstmt.setInt(1, number); int num = pstmt.executeUpdate(); // flush(); return num; } catch (SQLException e) { e.printStackTrace(); } return -1; } public int insert(String password, int balance) { Connection conn = DBConnection.getInstance(); try { String sql = "insert into account_tb(password, balance, created_at) values(?, ?,now())"; PreparedStatement pstmt = conn.prepareStatement(sql); // 코드는 다르지만 얘가 버퍼임 // 연결된 선에다가 버퍼를 달고 쿼리를 넣는다. 그런데 그 쿼리가 완성되지 않았어. // insert pstmt.setString(1, password); // 몇 번째 파라미터를 완성할거냐. 1, 2, 3,... 0이 없음 pstmt.setInt(2, balance); // 몇 번째 파라미터를 완성할거냐. 1, 2, 3,... 0이 없음 int num = pstmt.executeUpdate(); // flush(); return num; } catch (SQLException e) { e.printStackTrace(); } return -1; } public int updateByNumber(int balance, int number) { Connection conn = DBConnection.getInstance(); try { String sql = "update account_tb set balance = ? where number = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); // update pstmt.setInt(1, balance); pstmt.setInt(2, number); int num = pstmt.executeUpdate(); // flush(); return num; } catch (SQLException e) { e.printStackTrace(); } return -1; } public Account selectByNumber(int number) { // 계좌번호는 프라이머리키이기 때문에 하나만 있으니까 클래스에 담에서..... 요런 느낌. Connection conn = DBConnection.getInstance(); try { String sql = "select * from account_tb where number =?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, number); ResultSet rs = pstmt.executeQuery(); // ResultSet: 테이블 형태의 데이터 // Account rs = pstmt.executeQuery(); // 로 하지 않은 이유!! 뭐가 어떻게 만들어질지 알고? 일단 ResultSet으로 받아야 한다. // boolean isRow = rs.next(); // 커서 한 칸 내리기, boolean이다. 커서를 내렸는데 뭐가 있으면 true, 없으면 false // System.out.println(isRow); while (rs.next()){ // Account 클래스에 담는다. 그러지 않으면 테스트코드에서 또 rs어쩌구 해야함. // 계좌가 최초로 생성될 때는 생성자로 아래의 값들을 만든다. Account account = new Account( rs.getInt("number"), rs.getString("password"), rs.getInt("balance"), rs.getTimestamp("created_at") ); return account; } // // 커서를 한 칸 옆으로 옮기는 것! 프로젝션: 컬럼을 골라내는 것 // System.out.println(rs.getInt("number")); // System.out.println(rs.getString("password")); // System.out.println(rs.getInt("balance")); // System.out.println(rs.getTimestamp("created_at")); } catch (Exception e) { e.printStackTrace(); } return null; } public List<Account> selectAll() { Connection conn = DBConnection.getInstance(); try { String sql = "select * from account_tb order by number desc"; // 디폴트는 오름차순, 최근에 만든 것부터 보려면 내림차순! order by number desc 해줘야 함. PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery(); // ResultSet: 테이블 형태의 데이터 /** * 와일문을 돌면 어카운트가 하나씩 만들어지는데 그것을 add하면 account는 이미 담겼기 때문에 지워져도 되지만 어카운트리스트는 계속 새로 만들어지면 안 됨. */ List<Account> accountList = new ArrayList<>(); while (rs.next()){ // Account 클래스에 담는다. 그러지 않으면 테스트코드에서 또 rs어쩌구 해야함. // 계좌가 최초로 생성될 때는 생성자로 아래의 값들을 만든다. Account account = new Account( rs.getInt("number"), rs.getString("password"), rs.getInt("balance"), rs.getTimestamp("created_at") ); accountList.add(account); } return accountList; // // 커서를 한 칸 옆으로 옮기는 것! 프로젝션: 컬럼을 골라내는 것 // System.out.println(rs.getInt("number")); // System.out.println(rs.getString("password")); // System.out.println(rs.getInt("balance")); // System.out.println(rs.getTimestamp("created_at")); } catch (Exception e) { e.printStackTrace(); } return null; } }
main - db - DBConnection
package db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBConnection { public static Connection getInstance(){ String username = "root"; String password = "1234"; String url = "jdbc:mariadb://127.0.0.1:3306/cosdb2"; // 프로토콜이 적용된 소켓 try { Connection conn = DriverManager.getConnection(url, username, password); System.out.println("db connect success"); return conn; } catch (Exception e) { e.printStackTrace(); } return null; } }
main - model - Account
package model; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; import java.sql.Timestamp; /** * DB에 select 한 데이터를 담기 위한 오브젝트 */ @ToString @AllArgsConstructor // 모든 생성자를 자동으로 만들어 준다. @Getter public class Account { private int number; private String password; private int balance; // java.sql의 Timestamp // 카멜표기법 사용하기 private Timestamp createdAt; // 데이터에서 create_at이라고 되어 있지만 createAt으로 해야함. }
test
test - dao - BankDAOTest
package dao; import db.DBConnection; import model.Account; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; public class BankDAOTest { @Test public void deleteByNumber_test() { // given int number = 100; // when BankDAO dao = new BankDAO(); int result = dao.deleteByNumber(number); // then if (result == 1) { System.out.println("삭제 성공"); } else if (result == 0) { System.out.println(number + "번호를 찾을 수 없습니다."); } else { System.out.println("삭제 실패"); } } @Test public void insert() { // given String password = "1234"; int balance = 5000; // when BankDAO dao = new BankDAO(); int result = dao.insert(password, balance); // then if (result == 1) { System.out.println("삽입 성공"); } else if (result == 0) { System.out.println("password를 확인해 주세요."); } else { System.out.println("삽입 실패"); } } @Test public void updateByNumber_test() { // given int balance = 7000; int number = 1; // when BankDAO dao = new BankDAO(); int result = dao.updateByNumber(balance, number); // then if (result == 1) { System.out.println("업데이트 성공"); } else if (result == 0) { System.out.println("계좌번호를 확인해 주세요."); } else { System.out.println("업데이트 실패"); } } @Test public void selectByNumber_test() { // given int number = 3; // when BankDAO dao = new BankDAO(); Account account = dao.selectByNumber(number); // then if (account == null) { System.out.println("조회된 값이 없습니다."); } else { System.out.println(account); // @ToString를 써서 자동으로 투스트링 만들어짐. // System.out.println(account.getNumber()); // System.out.println(account.getPassword()); // System.out.println(account.getBalance()); // System.out.println(account.getCreatedAt()); } } @Test public void selectAll_test() { // given // when BankDAO dao = new BankDAO(); List<Account> accountList = dao.selectAll(); System.out.println(accountList.size()); // then } }
test - db - DBConnectionTest
package db; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBConnectionTest { @Test public void getInstance_test() { // given: 파라미터(테스트에는 파라미터를 못 적는다. 그래서 여기서 파라미터를 쓴다?) // when: 본코드 실행, 본코드의 메서드를 바로 호출한다. Connection conn = DBConnection.getInstance(); // 리턴되는 값 conn // then: 눈 검증 if(conn ==null){ System.out.println("실패"); } else { System.out.println("성공"); } } }
Share article