PreparedStatement와 CallableStatement의 차이
PreparedStatement
- PreparedStatement는 Statement를 확장한다.
- prepared: 준비된 → 준비된 컴파일. 미리 컴파일되어 준비하고 있기 때문에 statement에 비해 성능이 좋다.
- 주로 매개변수 전달이 필요한 SQL 작성이나, FOR LOOP 등으로 여러 번 SQL을 실행하는 경우에 사용한다.
String sql = "UPDATE java_member SET name=?, tel=?, pw=? WHERE id=?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, name);
pstmt.setString(2, tel);
pstmt.setString(3, pw);
pstmt.setString(4, id);
int re = pstmt.executeUpdate();
CallableStatement
- CallableStatement는 미리 작성해둔 프로시저를 실행하는 데 사용한다.
- CallableStatement는 PreparedStatement를 확장한다.
- ? 매개변수 자리에 프로시저에서 사용하는 매개변수와 가져올 값에 대한 세팅을 동시에 한다.
- 프로시저 실행에 필요한 매개변수는 setString, setInt 등으로 지정하고, 프로시저 응답으로 받을 값들에 대한 데이터 타입을 registerOutParameter로 모두 지정해야 한다.
- CallableStatement도 미리 컴파일되어 DB에 저장되어 있기 때문에 성능상으로 이점이 있다.
String sql = "{call bbs_find(?,?)}";
CallableStatement cs = con.prepareCall(sql);
cs.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
cs.setString(2, writer);
cs.execute();
ResultSet rs = (ResultSet)cs.getObject(1);
CallableStatement
형식 및 구조
- 매개변수가 없는 저장 프로시저 호출
{call 프로시저명}
- IN 매개변수를 리턴하는 프로시저 호출
{call 프로시저명(?, ?, ...) }
CallableStatement 객체 생성
- CallableStatement 객체는 Connection의 prepareCall() 메서드에 의해 생성된다.
- 프로시저를 호출하는 query를 작성해서 prepareCall() 메서드의 인자값으로 넣어주면 된다.
CallableStatement cstmt = con.prepareCall("{call bbs_add(?, ?, ?)}");
예제1
bbs에 게시글을 등록하는 프로시저를 작성하고 JDBC를 통해서 해당 프로시저를 호출해보자.
[오라클 프로시저]
--bbs에 게시글을 등록하는 프로시저
CREATE OR REPLACE PROCEDURE bbs_add(
ptitle IN BBS.TITLE%TYPE,
pwriter IN BBS.WRITER%TYPE,
pcontent IN BBS.CONTENT%TYPE
)
IS
BEGIN
INSERT INTO BBS(no, title, writer, content, wdate)
VALUES(bbs_seq.NEXTVAL, ptitle, pwriter, pcontent, SYSDATE);
COMMIT;
END;
/
[자바]
public class CallableStatementTest {
public static void main(String[] args) throws Exception{
Scanner sc = new Scanner(System.in);
System.out.println("Title: ");
String title = sc.nextLine();
System.out.println("Writer: ");
String writer = sc.nextLine();
System.out.println("Content: ");
String content = sc.nextLine();
System.out.println(title+"/"+writer+"/"+content);
//1. driver 로딩, db연동
Connection con = DBUtil.getCon();
//2. 프로시저를 호출하는 문장 작성
String sql = "{call bbs_add(?, ?, ?)}"; //ptitle, pwriter, pcontent
//3. CallableStatement 객체 얻기
CallableStatement cstmt = con.prepareCall(sql);
//4. CallableStatement의 set 메서드를 사용하여 ?를 대체할 값 지정
//앞의 숫자는 ?의 순서이다.
cstmt.setString(1, title);
cstmt.setString(2, writer);
cstmt.setString(3, content);
//5. execute 쿼리 실행
cstmt.execute();
//6. db자원 반납
cstmt.close();
con.close();
System.out.println("게시글 등록 완료!!");
}
}

SELECT * FROM bbs ORDER BY no DESC;

예제2
- bbs에 저장된 내용들을 자바에서 출력하는 프로시저를 만들자.
- sys_refcursor를 이용하여 OUT 파라미터로 자바에 넘기면 자바에서 ResultSet으로 받을 수 있다.
[오라클 프로시저]
CREATE OR REPLACE PROCEDURE bbs_list(mycr OUT sys_refcursor) --OUT: 결과로 나갈 데이터
IS
BEGIN
OPEN mycr FOR
SELECT no, title, writer, content, wdate FROM bbs
ORDER BY no DESC;
END;
/
[자바]
public class CallableStatementTest2 {
public static void main(String[] args) throws Exception{
//1. 프로시저를 호출하는 문장 작성
String sql = "{call bbs_list(?)}";
//2. driver 로딩, db연동 //3. CallableStatement 객체 얻기
try(Connection con = DBUtil.getCon(); CallableStatement cs=con.prepareCall(sql);){
//try() 괄호에 문장을 입력하면, 그 값들은 close가 자동으로 되므로, 따로 close를 하지 않아도 된다.
//con.close()와 cs.close() 생략
//4. CallableStatement의 registerOutParameter 메서드를 사용하여
//CREATE PROCEDURE문에 OUT으로 정의된 ? (아웃 파라미터)의 데이터 유형을 등록
cs.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
//5. excute 쿼리 실행
cs.execute();
//6. ResultSet 객체 얻어오기
ResultSet rs = (ResultSet)cs.getObject(1); //cs 실행 결과를 Object로 받아 downcast
//7. ResultSet 객체 순회
while(rs.next()) {
int no=rs.getInt("no");
String title = rs.getString("title");
String writer = rs.getString("writer");
String content = rs.getString("content");
Date wdate = rs.getDate("wdate");
System.out.printf("%d\t%s\t%s\t%s\t%s\n", no,title,writer,wdate.toString(), content);
}
//8. 자원 반납
rs.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
}

예제3
bbs 게시글의 작성자 중 일부 문자가 포함된 작성자를 검색하는 프로시저를 만들어서 자바에서 검색이 가능하도록 만들어보자.
[오라클 프로시저]
CREATE OR REPLACE PROCEDURE bbs_find(
mycr OUT sys_refcursor,
pwriter IN BBS.WRITER%TYPE
)
IS
BEGIN
OPEN mycr FOR
SELECT no, title, writer, content, wdate FROM bbs
WHERE writer LIKE '%'||pwriter||'%'
ORDER BY no DESC;
END;
/
[자바]
public class CallableStatementTest3 {
public static void main(String[] args) throws Exception{
Scanner sc = new Scanner(System.in);
System.out.println("검색할 작성자: ");
String writer = sc.nextLine();
//1. driver로딩, db연동
Connection con = DBUtil.getCon();
//2. 프로시저를 호출하는 문장 작성
String sql = "{call bbs_find(?,?)}"; //mycr, pwriter
//3. CallableStatement 객체 얻기
CallableStatement cs = con.prepareCall(sql);
//4. CallableStatement의 registerOutParameter 메서드를 사용하여
//CREATE PROCEDURE문에 OUT으로 정의된 ?(아웃 파라미터)의 데이터 유형 등록
cs.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
//5. CallableStatement의 set 메서드를 사용하여 ?를 대체할 값 지정
cs.setString(2, writer);
//6. execute 쿼리 실행
cs.execute();
//7. ResultSet 객체 얻어오기
ResultSet rs = (ResultSet)cs.getObject(1); //첫 번째 물음표(커서)
//8. ResultSet 객체 순회(커서 순회)
while(rs.next()) {
int no=rs.getInt("no");
String title = rs.getString("title");
String writer2 = rs.getString("writer");
String content = rs.getString("content");
Date wdate = rs.getDate("wdate");
System.out.printf("%d\t%s\t%s\t%s\t%s\n", no,title,writer2,wdate.toString(), content);
}
//9. db자원 반납
rs.close();
cs.close();
con.close();
}
}
