하루가 너무 빨리 간다 😭
실습하면서 밖에 창문을 보면 하늘이 너무 맑아서 뛰쳐나가고 싶을 지경이다.
곧 있으면 벚꽃길이 펼쳐지겠지... "아..내년 봄엔 벚꽃길 말고 퇴근길을 걷자"🖥️
푸념은 여기까지 :)
본론으로 가자!
지난 시간엔 JDBC API를 통해, DB와 소프트웨어를 연결하고 데이터를 보내고, 수정하고, 삭제하는 방법을 배웠다.
오늘은 데이터를 받아오고 이를 출력하는 것에 대해서 배워보자.
- JDBC : select 메서드 만들기
- DAO, DTO
delete, update, insert
를 구현할 경우, 그 반환값은 int
로 받고, 이는 해당 쿼리의 작업성공을 의미한다.
그러나, select
을 통해선 출력된 데이터들을 받고 싶다. 이로 인해 select 메서드는 다른 구조를 가진다.
① 쿼리 작성
String sql = "select * from cafe_menu";
② 워크시트에 담고 반환받기
ResultSet rs = state.executeQuery(sql);
ResultSet
담아서 보낸다.③ 포인터 이동 : rs.next();
rs.next();
.next()
를 기존 행에서 다음 행으로 넘어간다.boolearn
으로 가리키는 행의 데이터 유무를 판단한다.④ Main에서 출력하기
while(rs.next()) {
int pid = rs.getInt("pid"); // pid컬럼값을 int로 꺼내오겠다.
String pname = rs.getString("pname");
int pprice = rs.getInt("pprice");
String iced = rs.getString("iced");
System.out.println(pid + " : " + pname + " : " + pprice + " : " + iced);
}
.next()
의 반환값을 응용하여 반복문을 만들 수 있다.프로그램은 main 위에 올라갔을 때 실행된다.
하지만 main 안에 모든 코드를 작성하게 되면, 각 코드 간의 결합도, 중복성, 가독성의 문제가 발생하게 된다.
이 문제를 해결하기 위해 우린 객체지향과 디자인패턴을 배웠고, 이 중 다형성과 캡슐화를 이용해서 실습했었는데 그때, 기본적으로 배운 것이 DAO 와 DTO 이다.
일단 두 단어는 다음의 의미가 있다.
DAO(Data Access Object) : DB의 데이터에 접근하기 위해 생성하는 객체
DTO(Data Transfer Object) : 로직이 없는 순수한 데이터 객체
구체적으로 DAO는 DB 자체에 접근해서 우리가 배웠던 데이터 생성, 수정, 삭제, 읽기 등을 수행하는 기능들을 가진 객체로 메서드 단위로 구성된다.
DAO는 UI처럼 직접 출력하지 않고, 오직 DB와 UI 사이를 중개해주는 역할을 하게 된다.
public Connection getConnection() throws Exception{
String dbURL = "URL";
String dbID = "ID";
String dbPW = "PW";
Connection con = DriverManager.getConnection(dbURL, dbID, dbPW);
return con;
}
public int insert(String pname, int price, String iced) throws Exception {
Connection con = this.getConnection();
Statement state = con.createStatement();
String sql
= "insert into cafe_menu values(cafe_seq.nextval, '"+pname+"', "+price+", '"+iced+"')";
int result = state.executeUpdate(sql);
con.commit();
con.close();
return result;
}
DTO는 DAO를 통해서 받아들이는 데이터를 보관하기 위해 사용한다.
예를 들어, 앞선 select에선, select을 main에서 DAO의 역할을 해서 한 번에 출력했었다.
하지만 DAO로 기능을 분리하게 되면 문제가 발생한다.
먼저 DB의 안전성을 위해 메서드 수행 시 close();
가 필연적이다.
그러나 ResultSet
으로 넘어오는 데이터는 연결을 끊을 시, 더 이상 접근할 수 없게 된다.
따라서 해당 데이터를 DTO라는 바구니에 연결이 닫히기 전 옮겨 담는다.
그렇게 옮겨 담은 데이터를 UI 쪽에 반환하여 사용한다.
public List<CafeMenuDTO> selectAll() throws Exception{
Connection con = this.getConnection();
Statement state = con.createStatement();
String sql = "select * from cafe_menu";
ResultSet rs = state.executeQuery(sql);
List<CafeMenuDTO> list = new ArrayList<>();
// (1) 데이터를 꺼내온다.
while(rs.next()) {
int pid = rs.getInt("pid");
String pname = rs.getString("pname");
int pprice = rs.getInt("pprice");
String iced = rs.getString("iced");
// (2) DTO 인스턴스에 담는다. - DTO는 Getter&Setter, 생성자가 모두 있어야 한다.
CafeMenuDTO dto = new CafeMenuDTO(pid, pname, pprice, iced);
// (3) 해당 DTO를 ArrayList에 추가한다.
list.add(dto); // ArrayList 안에 인스턴스를 하나씩 연결.
}
// (4) 연결을 닫고 반환한다.
con.close();
return list;
}
DTO로 데이터를 담고, 이를 ArrayList에 연결해둔다.
이때, ArrayList의 제네릭을 Object
로 설정하지 않는다. 물론 DTO 인스턴스를 다형성으로 인해 연결을 할 순 있다.
하지만 DTO의 기능(getter, setter)을 사용하기 위해선 다운 캐스팅을 해야 하므로 가독성과 편의성에 좋지 않아 사용하지 않는다.