JDBC 첫 번째 시간
[ 여기까지 온 이유 ]
처음 프로그램을 만들 때, DTO 인스턴스를 생성해 데이터를 넣고 ArrayList 에 주소를 저장했다.
이 방식은, RAM에 데이터를 저장하기 때문에 프로그램의 종료와 함께 데이터도 사라졌다.
곧, 데이터의 영구적 보관이 우리에게 놓인 문제였다.
문제의 해결법은 보조 기억 장치에 영구적으로 보관하는 것이었다.
그 방법론으로 첫 번째로 본 것이 File IO 방식이다.
그러나 위 방식은 보안, 중복, 무결성, 버그 등 문제가 많기에 안전한 데이터 보관을 위해 DB라는 파일 형식에 저장하게 되었다.
따라서 이를 조작, 관리하기 위해 DBMS라는 미들웨어의 사용법인 SQL을 배웠다.
따라서 이젠 프로그램을 통해 입력받는 데이터 저장 및 관리를 위해 DBMS와 우리가 자바로 만들 프로그램을 연결하고자 한다.
- JDBC
- 응용
자바에서 DB에 접속할 수 있도록 하는 API이다.
응용프로그램과 DBMS 간의 통신을 중간에서 번역해준다.
사용하는 데이터베이스 종류와 상관없다.
Properties -> Java Build Path -> Classpath -> Add External JARs...
DBMS는 데이터를 관리하는 하나의 서버이다. 따라서 자바 프로그램으로 DB를 이용하려면,
File IO에서 배운 것처럼 서로의 네트워크 프로토콜을 따라야 한다.
그러나 이는 쉬운 일이 아니고, 우리가 굳이 알 필요가 없다.
왜냐면 오라클에서 그 프로토콜을 이용할 수 있는 라이브러리를 제공해준다.
좀 더 정확히 말하면 JDBC에 연결되는 자사 프로그램의 드라이버 클래스를 제공한다.
JDBC를 통해서 DB와 연결하기 전에, 한 가지 설정을 다시 봐야 한다.
위 차이는 불리는 메서드(Callee) 부르는 메서드(Caller)의 차이에서 발생한다.
Callee 는 단순 작업만 하면 된다. 예외가 발생한다면 불린 그 자리에서 처리하는 것이다.
주로 Caller인 main에서 처리해주면 된다.
물론 메서드가 메서드를 호출할 때도 있는데, 그것도 마찬가지로 Caller에서 처리한다.
따라서 메서드를 호출하는 Main에서 try-catch
로 예외 책임을 지고 어디에도 불리지 못하는 main은 예외 전가를 해선 안 된다.
오라클에서 제공한 DBMS 드라이버를 인스턴스를 생성한다.
이를 통해 DBMS와 연결이 된다.
Class.forName("oracle.jdbc.driver.OracleDriver"); // Reflection API
Reflection 기법으로 오라클 드라이버 JVM에 띄워둔다.
이때, 해당 클래스가 없을 수도 있기에 예외가 발생하니 처리하도록 한다.
DB 접속을 위해, 계정과 비밀번호, URL를 입력하는 과정
Connection con = DriverManager.getConnection("URL", "ID", "PW");
DriverManager
를 통해 DBMS 서버에 접근해 로그인되면 Connection
이라는 연결 인스턴스가 반환된다.
이제 연결을 통해 우리가 DB 개발도구처럼 워크시트를 생성해서 쿼리를 입력하고
반환을 받게 된다.
Statement 는 쿼리를 입력하는 워크시트 인스턴스이다.
Statement stat = con.createStatement();
생성된 워크시트 인스턴스를 가지고 쿼리를 입력하고 DB를 동작할 수 있다.
int result
= stat.executeUpdate("insert into cafe_menu values(cafe_seq.nextval, 'Americano', 1500, 'Y')");
// 쿼리 결과 확인
if (result > 0) {
System.out.println("쿼리 성공");
}
executeUpdate
메서드는 insert, update, delete처럼 실 DB에 영향을 주는 쿼리에서 사용한다.
따라서 성공 시, 그 결과를 int값으로 반환하고 이를 활용하여 콘솔로 결과를 받아볼 수 있다.
java8 부터는 자동 커밋을 지원한다.
하지만 insert, update, delete와 같이 DB에 실 영향을 주는 쿼리는 트랜잭션에 그 명령이 묶여있어서 commit
을 해줘야 한다.
con.commit();
DBMS라는 서버는 기본 동접 100명 정도 유지하는데, 이 접속은 프로그램을 종료한다고 해서 함께 종료되는 것이 아니다.
따라서 응용프로그램 쪽에서 해당 Connection 연결을 닫아줘야 한다.
con.close();
이제 위의 개발 과정을 통해서 DML을 응용해보자.
insert, update, delete는 쿼리문의 차이만 있을 뿐이다.
그리고 DB에 영향을 주기 때문에 executeUpdate를 사용한다.
Connection con = DriverManager.getConnection("URL", "ID", "PW");
Statement stat = con.createStatement();
int result = stat.executeUpdate("insert into cafe_menu values(1, 'Americano', 1500, 'Y')");
if (result > 0) {
System.out.println("쿼리 성공");
}
con.commit();
con.close();
int result
= stat.executeUpdate("update cafe_menu set pname='Cafe Latte', pprice=3000 where pid=1011");
if(result>0) {
System.out.println("쿼리 성공");
}
int result = stat.executeUpdate("delete from cafe_menu where PID=1012");
if (result>0) {
System.out.println("쿼리 성공");
}
앞선 DML은 DB에 반영된 작업의 성공수를 int값으로 반환한다.
하지만 결과를 출력하는 용도로 사용하는 select은 작업의 성공이 아닌
결과를 반영해야 하는데, int로 반영하면 우리는 알 수 없다.
따라서 select은 작업결과를 ResultSet(결과집합)
이라는 인스턴스로 반환한다.
.next();
를 이용해 다음 데이터의 주소로 간다..next();
Statement state = con.createStatement();
String sql = "select * from cafe_menu";
ResultSet rs = state.executeQuery(sql); // 데이터에 영향을 미치지 않는 쿼리는 executeQuery을 사용
while(rs.next()) { // 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);
}
con.close();
이제 DB를 통해서 데이터를 입력하고 검색하고 수정할 수 있고 프로그램이 꺼져도 사라지지 않게 되었다.
다음 시간엔 JDBC로 main에 작성된 코드들을 DAO로 메서드 단위로 빼는 리팩토링을 해보도록 하자.