본문 바로가기

수업 정리

26일차 수업 정리

**DTO & DAO 패턴

1. DTO : 여러개의 데이터를 하나로 표현하기 위한 클래스

    => DTO를 만드는 대시넹 Map을 사용하기도 함

2. DAO : 데이터베이스 작업만 처리하는 별도의 클래스

3. Books 테이블의 처리를 위해서 DTO 역할을 수행할 Book이라는 클래스와 DAO역할을 수행할 BookDAO 클래스를 만들고 수행할 작업을 위한 메소드를 선언

 

4. 실행을 위한 main메소드를 소유한 실행 클래스를 만들고 인터페이스를 작성

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);
		
	//이름 명시시 break로 한번에 나갈 수 있음
	mainloop: while(true) {
		System.out.print("메뉴 입력(1.전체데이터 보기 2.코드로 데이터 조회 3.데이터 삽입 4.데이터 수정 5.데이터 삭제 6.종료) : ");
		String menu = sc.nextLine();
		
		switch (menu) {
		case "1":
			System.out.println("전체 데이터 출력");
			break;
		case "2":
			System.out.println("코드로 데이터 조회");
			break;
		case "3":
			System.out.println("데이터 삽입");
			break;
		case "4":
			System.out.println("데이터 수정");
			break;
		case "5":
			System.out.println("데이터 삭제");
			break;
		case "6":
			System.out.println("종료");
			break mainloop;
		default:
			System.out.println("잘못된 입력입니다! 다시하세요!!!!!!");
			break;
		}

	}
		
	sc.close();
}

 

5. BookDAO 클래스에 공통으로 사용할 내용들을 작성

  1) 드라이버 클래스 로드

    => 애플리케이션 시작시 1회만 수행

    => 이런 코드는 애플리케이션의 EntryPoint를 찾아서 작성

        - java Application : main메소드를 소유한 클래스의 main 메소드

        - Android : MainActivity onCreate메소드

        - iOS : AppDelegate 클래스의 didFinishLaunchingWithOptions

    => 자바에서는 클래스 안에 static{ 코드 }를 작성하면 클래스가 로드될 때 1번만 수행

    => main 메소드를 소유한 클래스에 static 초기화 코드를 작성

  2) BookDAO 클래스에 공통으로 사용할 속성(멤버 변수, attribute, field, property)선언

    => 여러 메소드가 같이 사용

    => 데이터베이스 사용시 사용했던 클래스 : Connection, PreparedStatement

          (공통으로 사용할 

 

//메소드 들에서 공통으로 사용할 메소드

private Connection con;

private PreparedStatement pstmt;

 

  3) BookDAO 클래스에 데이터베이스 연결과 해제 메소드를 생성

    => 데이터 베이스 작업을 할 때마다 데이터 베이스 연결하고 작업하고 해제를 수행

    => 연결과 해제는 매번 동일한 동작

       (이렇게 여러곳에서 동일한 동작 수행시는 이 내용을 메소드로 만들어서 사용하는 것이 좋음)

//데이터 베이스 연결 메소드
private void connect() {
	try {
		Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.200:1521:xe", "user08", "user08");
	}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());
	}
}

 

4. 테이블의 전체 데이터 가져오기 구현

    => SQL: select * from books;

  1) BookDAO

public List<Book> allBook() {
	//리턴할 데이터 생성
	//여러개일 때는 생성자를 호출하여 인스턴스를 생성
	List<Book> list = new ArrayList<Book>();

	// 데이터베이스 연결
	connect();
	// SQL 실행 객체 생성
	try {
		pstmt = con.prepareStatement("select * from books");
		//SQL 실행
		ResultSet rs = pstmt.executeQuery();
		//rs에 들어가는 데이터가 여러개인지 확인(여러개임)
		while(rs.next()) {
			//행단위 작업 수행
			Book imsi = new Book();
			//ISBN 열의 값을 문자열로 읽어서 imsi에 저장
			//Mybatis나 Hibernate를 사용할 때 변수 이름만 설정하면 이 작업은 필요 없음
			imsi.setISBN(rs.getString("ISBN"));
			imsi.setBookName(rs.getString("BookName"));
			imsi.setAuthor(rs.getString("Author"));
			imsi.setCompany(rs.getString("Company"));
			imsi.setPrice(rs.getInt("price"));
			imsi.setPubDate(rs.getDate("pubDate"));
				
			//list에 삽입
			list.add(imsi);
		}
	} catch (Exception e) {
		System.err.println("SQL 실행 에러");
		System.err.println(e.getMessage());
	}
	// 데이터베이스 연결 해제
	close();

	return list;
}

  2) BookMain에서 1번 메뉴를 눌렀을 때 전체 데이터를 가져와서 출력하는 코드 작성

    => switch 구문 외부에 BookDAO 인스턴스를 생성하는 코드 작성

//안만들어지면 abstract, interface인지 확인
//아니면 자기 자신을 호출하는 메서드가 있는지 확인
//BookDAO 인스턴스 생성(Singleton)
BookDAO dao = BookDAO.sharedInstance();

case "1":
	System.out.println("전체 데이터 출력");
	// 전체 데이터를 가져오는 메소드 호출
	list = dao.allBook();
	for (Book b : list) {
		System.out.println("ISBN : " + b.getISBN());
		System.out.println("책제목 : " + b.getBookName());
		System.out.println("작가 : " + b.getAuthor());
		System.out.println("출판사 : " + b.getCompany());
		System.out.println("가격 : " + b.getPrice());
		System.out.println("출시일 : " + b.getPubDate() + "\n");
	}
	break;

5. 상세보기 구현

    => 기본키의 값을 입력 받아서 하나의 데이터를 찾아온 후 전체를 출력

    => SQL : SELECT * FROM BOOK WHERE ISBN = ?

  1) BookDAO 클래스의 getBook 메소드를 작성

  2) 상세보기를 처리하기 위한 코드를 main 메소드의 case 2에 작성

case "2":
	System.out.println("코드로 데이터 조회");
	System.out.print("코드를 입력하시오 : ");
	search = sc.nextLine();

	// dao 메서드 호출
	book = dao.getBook(search);

	if (book == null) {
		System.out.println("해당 ISBN은 존재하지 않습니다.");
	} else {
		System.out.println("ISBN - " + search + "의 검색결과");
		System.out.println("ISBN : " + book.getISBN());
		System.out.println("책제목 : " + book.getBookName());
		System.out.println("작가 : " + book.getAuthor());
		System.out.println("출판사 : " + book.getCompany());
		System.out.println("가격 : " + book.getPrice());
		System.out.println("출시일 : " + book.getPubDate() + "\n");
	}
	break;

  3) char 사용시 주의 사항

    => char는 처음 설정한 크기가 변하지 않음

    => 설정한 크기보다 글자수가 작은 데이터를 저장시 뒤에 공백이 발생

    => 다른 데이터와 비교할 때, trim()을 사용하여 좌우 공백을 제거

    => GoodDAO SQL을 수정

  4) 중요한 것

    => 기본키를 가지고 데이터 조회시 1 or 0개만 리턴

    => 기본키를 가지고 데이터 조회시 존재하면 인스턴스를 만들어서 리턴, 그렇지 않으면 null리턴

    => char을 가지고 조회시 반드시 좌우 공백에 주의 해야함(char trim()을 이용하여 작업)

 

6. 데이터 삽입 구현

    => 기본키의 값을 어떻게 저장 할 것인지를 결정

    => 기본키의 값을 시퀀스나 auto_increment를 이용하여 저장하는 경우, 사용자로부터 입력받는 경우 중 선택

    => 직접 입력 받는 경우 중복 검사를 먼저 수행

    => 삽입하는 SQL : INSERT INTO 테이블명(컬럼명 나열) VALUES(데이터 나열)

        ex) INSERT INTO BOOKS(ISBN, BOOKNAME, AUTHOR, COMPANY, PRICE, PUBDATE) VALUES(?,?,?,?,?,?)

  1) BookDAO의 삽입 메소드 구현

	// 데이터를 삽입하는 메소드
	// 삽입이나 수정은 매개변수가 DTO 아니면 Map
	// -1이 리턴되면 실패, 0이 리턴되면 조건에 맞는 데이터가 없음, 양수 리턴시 작업을 수행
public int insertBook(Book book) {
	int result = -1;
	// 데이터 베이스 연결
	connect();

	// 데이터베이스 작업
	try {
		// INSERT INTO BOOK VALUES(ISBN, BOOKNAME, AUTHOR, COMPANY, PRICE, PUBDATE)
		pstmt = con.prepareStatement(
				"INSERT INTO BOOK(ISBN, BOOKNAME, AUTHOR, COMPANY, " + "PRICE, PUBDATE) VALUES(?, ?, ?, ?, ?, ?)");
		pstmt.setString(1, book.getISBN());
		pstmt.setString(2, book.getBookName());
		pstmt.setString(3, book.getAuthor());
		pstmt.setString(4, book.getCompany());
		pstmt.setInt(5, book.getPrice());
		pstmt.setDate(6, book.getPubDate());
			// SQL 실행
		result = pstmt.executeUpdate();
		if (result < 0) {
			System.out.println("데이터 삽입 실패");
		} else if (result == 0) {
			System.out.println("조건에 맞는 데이터가 없음");
		} else {
			System.out.println("데이터 삽입 성공");
		}
	} catch (Exception e) {
		System.err.println("SQL 삽입 에러");
		System.err.println(e.getMessage());
	}

	// 데이터베이스 연결 해제
	close();

	return result;
}

 

  2) BookMain case3번 코드 작성

//BookMain
case "3":
	System.out.println("데이터 삽입");
	System.out.print("ISBN를 입력하시오 : ");
	ISBN = sc.nextLine();
	book = dao.getBook(ISBN);

	if (book == null) {
		System.out.println("입력 할수 있습니다.");
		System.out.print("책제목을 입력하시오 : ");
		bookName = sc.nextLine();
		System.out.print("작가을 입력하시오 : ");
		author = sc.nextLine();
		System.out.print("출판사을 입력하시오 : ");
		company = sc.nextLine();
		System.out.print("가격을 입력하시오 : ");
		price = sc.nextLine();

		// 삽입할 데이터 생성
		book = new Book();
		book.setISBN(ISBN);
		book.setBookName(bookName);
		book.setAuthor(author);
		book.setCompany(company);
		book.setPrice(Integer.parseInt(price));
		book.setPubDate(pubDate);

		dao.insertBook(book);
	} else {
		System.out.println("존재하는 ISBN입니다.");
	}
	break;

7. 데이터 수정 구현

    => 기본키를 가지고 데이터를 조회해서 데이터를 찾은 후 데이터가 있으면 수정할 내용을 입력받고 데이터 수정

    => SQL : UPDATE 테이블명 SET 수정할 열이름 = ?, ...WHERE 기본키 = ?;

    => 수정에 성공하면 수정된 행의 갯수를 리턴

  1) BookDAO의 데이터 수정 메소드 작성

public int updateBook(Book book) {
		int result = -1;
		// 데이터 베이스 연결
		connect();

		// 데이터베이스 작업
		try {
			// UPDATE BOOK SET ? = ?
			pstmt = con.prepareStatement("UPDATE BOOK SET BOOKNAME = ?, AUTHOR = ?, "
					+ "COMPANY = ?, PRICE = ? where isbn = ?");
			pstmt.setString(1, book.getBookName());
			pstmt.setString(2, book.getAuthor());
			pstmt.setString(3, book.getCompany());
			pstmt.setInt(4, book.getPrice());
			pstmt.setString(5, book.getISBN());

			// SQL 실행
			result = pstmt.executeUpdate();

			if(result > 0) {
				System.out.println("데이터 수정 성공");
			}else if(result ==0) {
				System.out.println("조건에 맞는 데이터 없음");
			}else {
				System.out.println("수정 실패");
			}
		} catch (Exception e) {
			System.err.println("SQL 수정 에러");
			System.err.println(e.getMessage());
		}

		// 데이터베이스 연결 해제
		close();
		return result;
	}

 

  2) main 메소드의 case 4번 작성

case "4":
	while (true) {
		System.out.println("데이터 수정");
		System.out.print("수정할 코드 입력 : ");
		ISBN = sc.nextLine();
		book = dao.getBook(ISBN);

		// 데이터 존재하는 경우
		if (book != null) {
			System.out.print("수정할 책 이름 : ");
			bookName = sc.nextLine();
			System.out.print("수정할 작가 명 : ");
			author = sc.nextLine();
			System.out.println("수정할 출판사 명 : ");
			company = sc.nextLine();
			System.out.println("수정할 가격 : ");
			price = sc.nextLine();

			book.setBookName(bookName);
			book.setAuthor(author);
			book.setCompany(company);
			book.setPrice(Integer.parseInt(price));

			dao.updateBook(book);

			break;
		} else {
			System.out.println("수정할 수 없는 코드입니다.");
		}
	}
	break;

 

  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이 리턴

 

  1) BookDAO의 데이터 삭제 메소드 작성

// 데이터를 삭제하는 메소드
// 삭제시는 대부분 기본키만으로 수행
public int deleteBook(String ISBN) {
	int result = -1;
	connect();
		
	try {
		pstmt = con.prepareStatement("DELETE FROM BOOK WHERE trim(ISBN) = ?");
		pstmt.setString(1, ISBN);
			
		result = pstmt.executeUpdate();
	}catch (Exception e) {
		System.out.println("데이터 삭제 에러");
		System.err.println(e.getMessage());
	}
		
	close();
	return result;
}

 

  2) main 메소드의 case 5번 작성

case "5":
	System.out.println("데이터 삭제");
	System.out.print("삭제할 데이터의 ISBN을 입력하시오 : ");
	ISBN = sc.nextLine();
	book = dao.getBook(ISBN);
	if (book == null) {
		System.out.println("해당 ISBN은 존재하지 않습니다.");
	} else {
		// 대화상자 출력하여 묻기
		int r = JOptionPane.showConfirmDialog(null, "진짜 삭제 할거야?", "삭제", JOptionPane.YES_NO_OPTION);
		if (r == JOptionPane.YES_OPTION) {
			// 데이터 삭제
			result = dao.deleteBook(ISBN);
			if (result > 0) {
				JOptionPane.showMessageDialog(null, "삭제성공");
			}
		} else {
			// 데이터 삭제 명령 취소
			System.out.println("삭제 작업이 취소 됨");
		}
	}
	break;

9. bookName이나 author에 포함된 데이터 조회

    => 포함된 데이터를 찾을 때는 like '%포함될 문자열%'의 형태가 되어야 함

    => 영문이 포함된 경우 데이터 베이스와 입력한 검색어에서 대문자나 소문자로 만들어야 함

    => char를 검색 할 때는 양쪽 다 trim()을 이용해서 좌우 공백을 제거해야 함

  1) Book 클래스에 입력한 단어가 bookName이나 author에 포함된 데이터를 전부 조회하는 메소드 구현

    public ? search(?){ }

//매개변수가 BookName이나 Author에 포함된 데이터를 조회하는 메소드
//List, DTO나 MAP, Scala(기본형, String, Date)
public List<Book> SearchBook(String str) {
	List<Book> list = new ArrayList<Book>();
	// 데이터 베이스 연결
	connect();

	// 데이터베이스 작업
	try {
		pstmt = con.prepareStatement("SELECT * FROM BOOK WHERE "
				+ "upper(BOOKNAME) LIKE ? OR upper(AUTHOR) LIKE ?");
		pstmt.setString(1, "%" + str.toUpperCase() + "%");
		pstmt.setString(2, "%" + str.toUpperCase() + "%");
		// SQL 실행
		ResultSet rs = pstmt.executeQuery();
		// rs에 들어가는 데이터가 여러개인지 확인(여러개임)
			
		while (rs.next()) {
			// 행단위 작업 수행
			//반복하면서 계속 생성해서 값을 채우고 리턴을 반복
			Book imsi = new Book();
			// ISBN 열의 값을 문자열로 읽어서 imsi에 저장
			// Mybatis나 Hibernate를 사용할 때 변수 이름만 설정하면 이 작업은 필요 없음
			imsi.setISBN(rs.getString("ISBN"));
			imsi.setBookName(rs.getString("BookName"));
			imsi.setAuthor(rs.getString("Author"));
			imsi.setCompany(rs.getString("Company"));
			imsi.setPrice(rs.getInt("price"));
			imsi.setPubDate(rs.getDate("pubDate"));

			// list에 삽입
			list.add(imsi);
		}
		rs.close();
	} catch (Exception e) {
		System.err.println("SQL 실행 에러");
		System.err.println(e.getMessage());
	}
	// 데이터베이스 연결 해제

	close();
	return list;
}

  2) main 메소드의 case 6번 작성

case "6":
	System.out.print("검색할 단어를 선택하시오 : ");
	bookName = sc.nextLine();
	System.out.println("검색 결과 출력");
	// 검색 데이터를 가져오는 메소드 호출
	list = dao.SearchBook(bookName);

	System.out.println("책 이름 or 작가명에 " + bookName + "이 포함된 검색 결과입니다.");
	for (Book b : list) {
		System.out.println("ISBN : " + b.getISBN());
		System.out.println("책제목 : " + b.getBookName());
		System.out.println("작가 : " + b.getAuthor());
		System.out.println("출판사 : " + b.getCompany());
		System.out.println("가격 : " + b.getPrice());
		System.out.println("출시일 : " + b.getPubDate() + "\n");
	}
	break;

**DAO 메소드

1. 리턴 타입

  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여부로 판단

  2) SELECT 이외 구문

    - insert, delete, update는 정수

    - create, alter, drop, revoke void - 리턴안함

 

2. 메소드의 매개변수

    => insert update Map아니면 DTO

    => Delete는 기본키

    => 전체 데이터 가져오기는 메개변수 X

    => 페이지 단위로 가져올 경우 검색어, 페이지 번호, 페이지당 데이터 갯수

    => 상세보기는 기본키

    => 로그인 같은 경우는 id pw

 

**java.util.Map - 인터페이스

    => Map을 구현한 클래스 : HashMap, Linked(순서)HashMap, Tree(정렬)Map

    => Map key(이름) Value() 쌍으로 저장하는 자료구조

    => key Value의 자료형은 모든 자료형이 가능하지만 key는 특별한 경우가 아니면 String

 

1. 생성

Map<String, Object> 변수명 = new HashMap<>();

 

2. 데이터 저장

    => 형식 : Map.put(, 데이터);

    => key는 중복해서 저장 할 수 없기 때문에 동일한 Key에 데이터를 저장하면 수정 됨

3. 데이터 가져오기

    => 형식 : Map.get()

    => Key에 해당하는 데이터를 가져오는데 없는 키 사용시 null이 리턴됨

4. 데이터 삭제

    => 형식 : Map.remove()

5. 용도

    => DTO 클래스처럼 여러개의 데이터를 하나로 묶기 위해서 사용

Book book = new Book();
book.setISBN("001");
book.setBookName("딸기");
book.setAuthor("작가님");
book.setCompany("이 출판사");
book.setPrice(5000);
book.setPubDate(new Date(System.currentTimeInMillis()));

//Map이 HashMap의 상위 인터페이스라서 이렇게 작성
Map<String, Object> map = new HashMap<>();
map.put("ISBN", "001");
map.put("bookName", "딸기");
map.put("author", "작가님");
map.put("company", "출판사);
map.put("price", 5000);
map.put("pubDate", new Date(System.currentTimeInMillis()));
 
//출력만 하고자 하는 경우
System.out.println(map.get("price")); //5000이 출력

//price에 1000을 더하기
((Integer)map.get("price")) + 1000
    => 출력하는 경우가 아니면 형 변환하여 사용