26일차 공부 DTO&DAO
1 교시
** DTO & DAO 패턴
1. DTO : 여러 개의 데이터를 하나로 표현하기 위한 클래스
=>DTO를 만드는 대신에 Map을 이용하기도 한다.
2. DAO : 데이터베이스 작업만 처리하는 별도의 클래스
3. Goods 테이블의 처리를 위해서 DTO 역할을 수행할 Good 이라는 클래스와 DAO 역할을 수행할 GoodDAO 클래스를 만들고 수행할 작업을 위한 메소드를 선언
4. 실행을 위한 main 메소드를 소유한 실행 클래스를 만들고 인터페이스를 작성
package javaapp0513; import java.util.Scanner; public class GoodMain { public static void main(String[] args) { } |
5.GoodDAO 클래스에 공통으로 사용할 내용들을 작성
1) 드라이버 클래스 로드
=>애플리케이션이 시작할 때 1번만 수행되면 됩니다.
=>이런 코드는 애플리케이션의 entry point를 찾아서 작성
=>java application은 entry point 가 main 메소드를 소유한 클래스의 main 메소드
android 는 MainActivity의 onCreate 메소드, iOS는 AppDelegate 클래스의 didFinishLaunchingWithOptions
=>자바에서는 클래스 안에 static{ 코드 }를 작성하면 클래스가 로드 될 때 1번만 수행됩니다.
=>main 메소드를 소유한 클래스에 static 초기화 코드를 작성
//static 초기화 static { try { Class.forName( "oracle.jdbc.driver.OracleDriver"); //System.out.println("드라이버 클래스 로드 성공"); } catch (ClassNotFoundException e) { System.out.println(e.getMessage()); e.printStackTrace(); } } |
2) GoodDAO 클래스에 공통으로 사용할 속성(멤버 변수, attribute, field, property 등) 선언
=>여러 메소드가 같이 사용
=>데이터베이스 사용시 사용했던 클래스 : Connection, PreparedStatement
// 메소드 들에서 공통으로 사용할 변수 private Connection con; private PreparedStatement pstmt; |
3) GoodDAO 클래스에 데이터베이스 연결과 해제 메소드를 생성
=> 데이터베이스 작업을 할 때 마다 데이터베이스 연결하고 작업하고 해제를 수행
=> 연결과 해제는 매번 동일한 동작이다.
이렇게 여러 곳에서 동일한 동작을 수행할 때는 이 내용을 메소드로 만들어서 사용하는 것이 좋다.
// 데이터베이스 연결 메소드 private void connect() { try { con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.200:1521:xe", "user05", "user05"); }catch (Exception e) { System.err.println("연결 실패"); System.err.println(e.getMessage()); } } // 데이터베이스 연결 해제 하는 메소드 private void close() { try { pstmt.close(); con.close(); }catch (Exception e) { System.err.println("연결 실패"); System.err.println(e.getMessage()); } } |
2~3 교시
4) 테이블의 전체 데이터 가져오기 구현
=> sql: select * from goods;
(1) GoodDAO 클래스에서 전체 데이터를 읽어오는 메소드 구현
public List allGood() { //list에 삽입
|
![]() |
(2) GoodMain에서 1번 메뉴를 눌렀을 때 전체 데이터를 가져와서 출력하는 코드를 작성
=> switch 구문 외부에 GoodDAO 인스턴스를 생성하는 코드를 작성
// GoodDAO 인스턴스 생성 GoodDAO dao = GoodDAO.sharedInstance(); |
package javaapp0513; import java.util.List; public class GoodMain { } |
메뉴 입력(1.전체 데이터 보기 2.코드로 데이터 조회하기 3.데이터 삽입 4.데이터 수정 5.데이터 삭제 6.종료):1 001 :바나나:3000원 002 :딸기:5000원 003 :망고:4000원 004 :파인애플:7000원 메뉴 입력(1.전체 데이터 보기 2.코드로 데이터 조회하기 3.데이터 삽입 4.데이터 수정 5.데이터 삭제 6.종료): |
![]() |
5) 상세보기 구현
=> 기본키의 값을 입력받아서 하나의 데이터를 찾아온 후 전체를 출력
=> sql : select * from goods where code = ?
(1) GoodDAO 클래스에 메소드 작성
public Good getGood(String code) { Good good = null; connect(); // 데이터 베이스 연결 try { // 데이터베이스 작업 pstmt = con.prepareStatement("select * from goods where code = ?"); pstmt.setString(1, code); ResultSet rs = pstmt.executeQuery(); // SQL 실행 //데이터가 2개 이상 나올수 없으므로 if로 처리 if(rs.next()) { good = new Good(); good.setCode(rs.getString("code")); good.setName(rs.getString("name")); good.setManufacture(rs.getString("manufacture")); good.setPrice(rs.getInt("price")); good.setReceivedate(rs.getDate("receivedate")); } }catch(Exception e) { System.err.println("상세보기 에러"); System.err.println(e.getMessage()); } close(); // 데이터베이스 연결 해제 return good; } |
2) 상세보기 처리하기 위한 코드를 main 메소드의 case2번에 작성
case "2": System.out.print("조회할 코드 입력:"); String code = sc.nextLine(); Good good1 = dao.getGood(code); if(good1 == null) { System.out.println("코드에 해당하는 데이터 없음"); }else { System.out.println(good1); } break; 위 코드로 작성하면 조회가 안되는데 이유는 trim으로 공백을 제거하지 않아서다. |
3) char 사용시 주의 사항
=> char 는 처음 설정한 크기가 변하지 않는다.
=> 설정한 크기보다 글자수가 적은 데이터를 저장하면 뒤에 공백이 발생한다.
=> 다른 데이터와 비교할 때 trim을 이용해서 좌우 공백을 제거해야 한다.
=> GoodDAO 의 sql을 수정
![]() |
![]() |
4) 중요한 것
=> 기본키를 가지고 데이터를 조회하면 1개 또는 0개만 리턴
=> 기본키를 가지고 데이터를 조회하면 존재하면 인스턴스를 만들어서 리턴하고 그렇지 않으면 null이 리턴
=> char 가지고 조회를 할 대는 반드시 좌우 공백을 주의해야 한다.
char 는 trim()을 이용해서 작업
4 교시
6) 데이터 삽입 구현
=> 기본키의 값을 어떻게 저장할 것인지를 결정
=> 기본키의 값을 시퀀스나 auto_increment를 이용해서 저장하는 경우와 사용자로부터 직접 입력받는 경우 중에서 선택을 해야함
=> 직접 입력받는 경우는 중복 검사를 수행해야 한다.
=> 삽입하는 SQL : insert into 테이블이름(컬럼이름나열) values(데이터나열)
insert into goods(code, name, manufacture, price, receivedate) values(?, ?, ?, ?, ?)
(1) GoodDAO 의 삽입 메소드 구현
public int insertGood() { int result = -1; try { pstmt = con.prepareStatement("insert into goods(code, name, manufacture, price, receivedate) values(?, ?, ?, ?, ?)"); pstmt.setString(1, good.getCode()); pstmt.setString(2, good.getName()); pstmt.setString(3, good.getManufacture()); pstmt.setInt(4, good.getPrice()); pstmt.setDate(5, good.getReceivedate()); // 실행 result = pstmt.executeUpdate(); }catch(Exception e) { System.err.println("상세보기 에러"); System.err.println(e.getMessage()); } return result; } |
(2) GoodMain의 case 3 번 코드 작성
// switch 구문 내에서 사용할 변수 switch (menu) { // Dao 메소드 호출 break; // 삽입할 데이터 생성 // sql 실행 |
5 교시
7) 데이터 수정 구현
=> 기본키를 가지고 데이터를 조회해서 데이터를 찾은 후 테이터가 있으면 수정할 내용을 입력 받고 데이터를 수정
=> sql : update 데이블이름 set 수정할 열이름 =?, ....where 기본키 = ?
=> 수정에 성공하면 수정 된 행의 개수를 리턴
(1) GoodDAO 클래스의 데이터 수정 메소드 작성
public int updateGood(Good good) { int result = -1; connect(); try { pstmt = con.prepareStatement("update goods set name=?, manufacture=?, price=? where code=?"); pstmt.setString(1, good.getName()); pstmt.setString(2, good.getManufacture()); pstmt.setInt(3, good.getPrice()); pstmt.setString(4, good.getCode()); result = pstmt.executeUpdate(); }catch(Exception e) { System.out.println("데이터 수정 에러"); System.out.println(e.getMessage()); } close(); return result; } |
(2) main 메소드의 case 4번 작성
case "4": while(true) { System.out.print("수정할 코드를 입력:"); code = sc.nextLine(); good = dao.getGood(code); if(good != null) { // 데이터가 존재하는 경우 System.out.print("수정할 이름:"); String name = sc.nextLine(); good.setName(name); System.out.print("수정할 원산지:"); String manufacture = sc.nextLine(); good.setManufacture(manufacture); // 숫자 데이터는 되도록이면 문자열로 받은 후 정수로 변환하는 것을 권장 System.out.print("수정할 가격:"); String imsi = sc.nextLine(); good.setPrice(Integer.parseInt(imsi)); result = dao.updateGood(good); if(result > 0) { System.out.println("데이터 수정 성공"); }else if(result == 0){ System.out.println("조건에 맞는 데이터 없음"); }else { System.out.println("데이터 수정 실패"); } break; }else { System.out.println("수정할 수 없는 코드입니다."); } } |
![]() |
다금바리로 수정 |
(3) 많이 발생한 에러
=> sql을 만들 때 물음표 개수만큼 데이터를 정확하게 바인딩을 해야 한다.
=> sql 작성할 때 sql의 예약어 앞 뒤는 공백이 있어야 한다.
예약어 바로 뒤에 ( 가 나오는 경우는 생략이 가능
=> 기본키에 영문이 있는 경우는 uppercase 또는 lowercase 그리고 trim을 사용해야 한다.
=> executeUpdate를 호출하지 않으면 sql 실행 안됨
=> where를 생략해서 모든 데이터가 변환되어 버림
8) 데이터 삭제 구현
=> 데이터 삭제하는 경우 필요한 데이터는 기본키
=> sql : delete from 테이블이름 where 기본키 = ?
=> 학제를 할 대는 정말로 삭제할 것인지 확인하는 절차를 거치는 것이 좋다.
int javax.swing.JOptionPane.showConfirmDialog(null, String 메시지, String 제목, JOptionPane.YES_NO+OPTION);
예 버튼을 누르면 JOptionPane.YES_OPTION이 리턴되고
아니오 누르면 JOptionPane.NO _OPTION이 리턴
6~7 교시
(1) GoodDAO 클래스의 데이터 삭제 메소드 작성
public int deleteGood(String code) { int result = -1; connect(); try { pstmt = con.prepareStatement("delete from goods where code =?"); pstmt.setString(1, code); result = pstmt.executeUpdate(); }catch(Exception e) { System.out.println("삭제 에러"); System.out.println(e.getMessage()); } close(); return result; |
(2) main 메소드의 case 5번 작성
case "5": System.out.print("삭제할 코드 입력:"); code = sc.nextLine(); good = dao.getGood(code); if(good == null) { System.out.println("없는 코드입니다."); }else { // 대화상자를출력해서 묻기 int r = JOptionPane.showConfirmDialog(null, "정말로 삭제", "삭제", JOptionPane.YES_NO_OPTION); if(r == JOptionPane.YES_OPTION) { result = dao.deleteGood(code); if(result > 0) { JOptionPane.showMessageDialog(null, "삭제 성공"); } } } break; |
![]() 실행하고 5번을 입력하면 이런 대화 클릭상자가 뜬다. |
8. name 이나 manufacture에 포함된 데이터 조회
=> 포함된 데이터를 찾을 대는 like '%포함될문자열%' 의 형태가 되어야 한다.
=> 영문이 포함된 경우는 데이터베이스에서 대문자나 소문자를 만들고 입력한 검색어도 대문자나 소문자로 만들어야 한다.
=> char를 검색할 때는 양쪽 다 trim()을 이용해서 좌우 공백을 제거해야 한다.
1) GoodDAO 클래스에 입력한 단어가 name이나 manufacture에 포함된 데이터를 전부 조회하는 메소드 구현
public ? search(?) {
}
// 매개변수가 name 이나 manufacture에 포하된 데이터를 조회하는 메소드 // List, DTO 나 MAP, Scala(기본형, String, Date) public List search(String word) { List list = new ArrayList(); // List는 생성해서 리턴 connect(); try { // pstmt = con.prepareStatement("select * from goods where upper(name) like ? or upper(manufacture) like ?"); pstmt.setString(1, "%" + word.toUpperCase() + "%"); // 영문일 경우 tpUpperCase 사용 pstmt.setString(2, "%" + word.toUpperCase() + "%"); ResultSet rs = pstmt.executeQuery(); while(rs.next()) { Good good = new Good(); good.setCode(rs.getString("code")); good.setName(rs.getString("name")); good.setManufacture(rs.getString("manufacture")); good.setPrice(rs.getInt("price")); good.setReceivedate(rs.getDate("receivedate")); list.add(good); } rs.close(); }catch(Exception e) { System.out.println("데이터 검색 에러"); System.out.println(e.getMessage()); } close(); return list; } |
case "6": System.out.print("이름이나 원산지를 입력:"); String word = sc.nextLine(); // 데이터베이스 처리 메소드 호출 List ar = dao.search(word); for(Good g : ar) { System.out.println(g); } break; case "7": System.out.println("프로그램 종료"); // mainloop 라는 반복문을 종료 break mainloop; default: System.out.println("잘못된 메뉴를 입력하셨습니다."); break; } |
![]() |
** DAO 메소드
1. 리턴 타입
=> select
열의 개수와 행의 개수를 파악
열 1개와 행1개 - scala data type(기본형, String, Date)
열 n개와 행1개 - Map 아니면 열 전체를 저장할 수 있는 DTO
열 1개이고 형n개 - scala data type의 List
열 n개이고 행n개 - Map 이나 DTO의 List
List인 경우는 null 이 리턴되면 안된다.
인스턴스를 만들고 리턴해야한다 : List는 반복문에 사용되기 때문이 null이면 NullPointerException이 발생
1개를 리턴해야하는 경우는 처음에 null이나 의미없는 값을 가지고 있다가 데이터가 조회가 되면 그 때 인스턴스를 생성해서 값을 대입
데이터 있고 없음을 null 여부로 판단
=> select 이외 구문
insert, delete, update는 정수
create, alter, drop, grant, revoke 는 void - 리턴안함
2. 메소드의 매개변수
=> insert 와 update는 Map 아니면 DTO
=> delete는 기본키
=> 전체 데이터 가져오기는 매개변수가 없다.
=> 페이지 단위로 가져올 때는 검색어, 페이지번호, 페이지당 데이터 개수
=> 상세보기는 기본키
=> 로그인 같은 경우는 id 와 password
8 교시
** java.util.Map -인터페이스
=> Map을 구현한 클래스 : HachMap, Linked(순서)HachMap, Tree(정렬)Map
=> Map은 Ley(이름) 와 Value(값) 쌍으로 저장하는 자료구조
=> Key와 Balue의 자료형은 모든 자료형이 가능하지만 Key는 특별한 경우가 아니면 String
1. 생성
Map<String, Object> 변수명 = new HashMap<>();
2. 데이터 저장
Map.put(키, 데이터);
=> key는 중복해서 저장할 수 없기 때문에 동일한 key에 데이터를 저장하면 수정이 된다.
3. 데이터 가져오기
Map.get(키)
=> key에 해당하는 데이터를 가져오는데 없는 key를 대입하면 null이 리턴된다.
4. 데이터 지우기
Map.remove(키)
5. 용도
=> DTO 클래스처럼 여러개의 데이터를 하나로 묶기 위해서 사용
Good good = new Good();
good.setCode("001");
good.setName("딸기");
good.setManufacture("논산");
good.setPrice(5000);
good.setReceiveDate(new Date(System.currentTimeInMillis());
Map<String, Object> map = new Hashmap<>();
map.put("code", "001");
map.put("name", " 딸기");
map.put("manufacture", "논산");
map.put("price", 5000);
map.put("receivedate", new Date(System.currentTimeInMillis());
//출력만 하고자 하는 경우
System.out.println(map.get("price")); //5000이 출력
// price에 1000을 더하기
((Integer)map.get("price")) + 1000 // Integer로 형변환을 해야만 출력이 된다.
public List<Map<String, Object>> allGood() { //리턴할 데이터 생성 //여러 개 일 때는 생성자를 호출해서 인스턴스를 생성 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); //데이터베이스 연결 connect(); try { //sql 실행 객체 생성 pstmt = con.prepareStatement( "select * from goods"); //sql 실행 ResultSet rs = pstmt.executeQuery(); //데이터가 여러 개 while(rs.next()) { //행 단위 작업 수행 Map<Map<String, Object> map = new HashMap<>(); //code 열의 값을 문자열로 읽어서 imsi에 저장 //MyBatis 나 Hibernate를 사용할 때 변수이름만 설정하면 //이 작업은 할 필요가 없음 //imsi.setCode(rs.getString("code")); //imsi.setName(rs.getString("name")); //imsi.setManufacture(rs.getString("manufacture")); //imsi.setPrice(rs.getInt("price")); //imsi.setReceivedate(rs.getDate("receivedate")); map.put("code", rs.getString("code")); map.put("name", rs.getString("name")); map.put("manucacture", rs.getString("manufacture")); map.put("price", rs.getInt("price")); map.put("receivedate", rs.getDate("receicedate")); //list에 삽입 list.add(map); } |
goodDAO 를 일부 수정해서 Map 구현 |
switch (menu) { case "1": // 전체 데이터 가져오는 메소드 호출 List> maplist = dao.allGood(); // list 순회하면서 출력하기 for (Map g : maplist) { Set keySet = g.keySet(); for(String key : keySet) { System.out.println(g.get(key) + "\t"); } } break; |
GoodMain에서 case1을 수정해서 Map 사용 |