DAO (Data Access Object)

yuzu sim's avatar
Jan 22, 2024
DAO (Data Access Object)

1. 데이터베이스의 DAO (Data Access Object) 를 왜 만드나?

notion image
INSERT, DELETE, UPDATE는 요청하면 영향 받은 행 (숫자, INT)를 MAIN에 return 하고, SELECT는 테이블을 MAIN에 return 해준다. ★그러나 테이블 테이터를 자바가 바로 못받으니 자바 오브젝트로 파싱해서 리턴★ (DB는 자바가 아니고 다른 언어니까 파싱!) 즉, table을 받기 위해 테이블이랑 똑같이 생긴 클래스를 만들어야 한다. (String은 .메소드(. = 객체 연결 연산자)를 사용하지 못해서 XXX) JSON기억하죠? 그런걸 model 폴더에 만들 것
💡
DAO에 SELECT, INSERT, DELETE, UPDATE 메소드를 만들어줄 것! (재사용 하기 위함)
💡
DAO와 DB는 요청해서 응답을 받으니까 소켓 통신이다
 

[ 공공데이터는 항상 JASON으로 응답 ]

notion image
공공데이터는 언어를 특정하지 않는다. url이 파이선으로 한 건지, 자바로한건지 잘 모르기 때문에 (=어떤 언어로든지 접근 가능해서) 공공데이터는 항상 jason으로 응답하고, 자바는 그걸 파싱해서 받는다
 

2. DAO 만들기

데이터베이스와 소프트웨어 간의 통신을 도와주는 중간 역할을 하는 객체 데이터베이스에 접근하여 CRUD(Create, Read, Update, Delete) 기능을 수행하고, 데이터베이스 트랜잭션 관리, 쿼리 실행, 데이터 매핑 등의 역할을 수행한다. (즉, 데이터베이스에 접근하여 데이터를 가져오거나 저장하는 역할)
이 DAO를 사용하여 재사용 할 수 있는 클래스로 만들자! SRP 단일 책임 원칙. 클래스의 책임을 만든다 = 레이어를 나눈다고 한다. (층을 나눠서 층마다 책임을 부여함)
notion image
이렇게 DAO(Data Access Object)를 만든다. → 만국공통. 무조건!!!
 

[ BankDAO에 메소드 ]

notion image
이렇게 DAO에 CRUD 메소드를 만들면,
notion image
이 그림대로 만들어진 것! !!DAO를 만들어야지 재사용 가능하니 꼭 만들자!!
 

3. model 패키지 만들기

[ model 패키지 ]

"model" 폴더는 데이터베이스와 관련된 클래스들을 모아두는 곳! 이 클래스는 테이블과 동일한 구조를 갖는 모델 클래스로 만들어지며, JSON 형식으로 데이터를 표현할 수 있다. 이 DAO 클래스는 일반적으로 "model" 폴더에 저장되어 관리됩니다.
notion image
notion image
💡
* Table을 받기 위해, Table과 똑같이 생긴 Account 클래스를 model 패키지에 만들어줌. * 그냥 막 만들지 말고, table 작성한 그 데이터로 만들어야 함. 이 데이터를 받을거라서! * 지금 디폴트 생성자 있는 상태니까... 안써도 됨. 나중에 필요하면 다시 쓰러 오자
💡
필드는 상태를 변경한다는 개념으로 쓰는게 아니라 파싱을 담는 항아리로 쓰인다. JSON > ‘파싱 담기’ 참조 즉, Account는 DB에 Select한 데이터를 담기 위한 오브젝트!
💡
* Timestamp = 날짜를 받을 땐 UTC로 받는 게 좋기 때문!
* createdAt 데이터베이스는 _ (언더스코어) 기법이지만 자바에선 카멜 표기법으로 써주기!
 

4. main에서 BankDAO 만들기

4-1. DELETE문

3-1. JDBC 자바와 마리아DB 연결에서 만들었던 그 코드를 재사용하기 위해 DAO를 만들려는 것 BankApp에 main에 있던 그 코드를 BankDAO의 deleteByNumber 메소드에 붙여넣는다.
notion image
이렇게 넣으면 무조건 number가 1인 계좌번호만 삭제되잖아. 그래서!! 메서드에 파라미터를 만들어준다.
notion image
 

 
notion image
만약, number에 2가 들어오면, getinstance()해서 소켓을 가져온다. (conn이 소켓이니까) 쿼리에 number 2를 넣는 코드 작성 (pstmt.setInt(1,number);) 그럼 쿼리를 소켓에 있는 버퍼에 담음 > 쿼리 완성 이제 플러시로 쏘자. (int num = pstmt.executeUpdate();)
데이터베이스가 number가 2번인 계좌를 리턴 할 때, 성공적이면 1을 리턴. 2가 없으면 0을 리턴. 때문에, return을 int num으로 받고, return 데이터 타입도 int로 바꿔준다. 만약 오류가 나면 catch로 넘어간다. throw하면 호출자한테 위임하게 되는데.... 그러면 너무 복잡해져서 오류 추적도 할 수 있는 e.printStackTrace()로 바꿀 것이다.
notion image
리턴 없어서 오류남. return 데이터 타입을 int로 바꿨으니, try를 탔을 때 reutrn값과 catch를 탔을 때의 return 값을 설정해줘야한다.
notion image
이제 오류나면 위임하는게 아니라 콘솔에 피범벅이 뜰 것!
💡
근데 이런 식으로 return하는거 쌤은 별로 좋아하지 않지만… 우리는 초보자니 이렇게 사용하자.

deleteByNumber 메소드 코드

public class BankDAO { public int deleteByNumber(int number) { Connection conn = DBConnection.getInstance(); try { String sql = "delete from account_tb where number = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1,number); int num = pstmt.executeUpdate(); return num; } catch (Exception e) { e.printStackTrace(); } return -1; }
 

4-2. INSERT문

이제 insert 만들어 보자! insert 쿼리문에서 number는 AUTO_INCREMENT라 작성하지 않는다.
notion image
자바에서 'now'가 아니라 `LocalDate`와 같은 로컬 데이트 타입을 사용하면 한국 시간으로 처리될 수 있다. 데이터베이스 서버와 자바 서버가 서로 다른 시간대에 위치한다면, 시간 정보가 일치하지 않을 수 있다는 뜻! ex) 자바 서버가 한국에 위치하고 데이터베이스 서버가 미국에 위치한다면, 시간대의 차이로 인해 데이터베이스에 저장된 시간 정보와 자바에서 사용하는 로컬 데이트 타입의 시간 정보가 다를 수 있다는 뜻 -> 망함
💡
데이터베이스 시간과 자바 서버 시간을 동기화하는 것이 중요
insert 했을 시, 1이 아니면 다 실패라고 볼 수 있다. 1이 아니면 제대로 적용된 게 아니기 때문! (0도 입력이 안 된거니까)

insert 메소드 코드

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); pstmt.setString(1,password); pstmt.setInt(2,balance); int num = pstmt.executeUpdate(); return num; } catch (Exception e) { e.printStackTrace(); } return -1; }
 

4-3. UPDATE문

notion image
쿼리문을 이렇게 작성하는건 너무 위험하다. 항상 + 1000만 할 것인지?
notion image

updateByNumber 메소드 코드

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); pstmt.setInt(1,balance); pstmt.setInt(2,number); int num = pstmt.executeUpdate(); return num; } catch (Exception e) { e.printStackTrace(); } return -1; }
 
 
 

5. test에서 BankDAOTest 테스트 해보기 [ given, when, then ]

6. main 코드에서 실행

test가 완료가 되면 main 코드에 실행해보자.
 

 
import dao.BankDAO; import db.DBConnection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Scanner; public class BankApp { public static void main(String[] args) { //이거 BR로도 만들 수 있다 Scanner sc = new Scanner(System.in); System.out.print("삭제할 계좌번호를 입력해주세요 : "); int number = sc.nextInt(); //DAO한테 위임 BankDAO dao = new BankDAO(); //결과를 int로 안받으면 잘 됐는지 안됐는지 고객이 알 수가 없음 int result = dao.deleteByNumber(number); if (result == 1) { System.out.println("삭제 성공했습니다"); } else { System.out.println("삭제 실패했습니다"); } } }
notion image
notion image
notion image
삭제 됨! 정상 작동!
 

[ 한글이 깨질 때 해결 방법 ]

notion image
notion image
여기 들어가서 설정 해 주자!
 
 

 
bankapp은 bankDAO를 통해 데이터베이스(DB)에 접근한다. 그런데 이 bankDAO는 DB에 접근할 때 DBConnection을 활용하여 필요한 작업을 수행한다. (DBConnection의 도움을 받아 가는 것!) bankDAO는 DBConnection을 호출하여 DB에 접근하고, DB로부터 반환(return)된 결과를 받는다. 이를 통해 bankDAO는 데이터베이스와의 상호작용을 처리한다. 즉, bankDAO가 미들웨어 격
 

TIP!

Impl 이라고 이름 붙은 건 Implements BankDAOImpl = Impl = implements 구현체 만들어라. 라는 뜻!
과거에 적었던 코드랑 테이블을 참조해서 deleteByNumber 메소드의 내용을 구현하자 코드는 안 보고 적는 것 아니다. 1. DB소켓 가져오고 2. 버퍼에 쿼리담고 3. 플러시하고 이런 과정을 이해하자!
💡
return 문을 사용하여 num 값을 반환하는 이유는 메서드가 호출된 곳에서 삭제된 레코드의 개수를 확인하고 처리 결과를 알기 위해서 즉, insert() 메서드를 호출하여 결과를 int 변수 result에 저장하는 것
notion image
Share article

Coding_study