Java

[JDBC] JDBC 필수 지식

유호야 2020. 12. 5. 21:32
반응형

JDBC 필수 지식

Statement

실제 데이터베이스에 쿼리를 보내기 위해 필요한 객체이다.
삽입, 수정, 삭제, 검색을 처리하는 DML문을 사용할 때는 꼭 이객체를 사용하여야 한다.
이 객체는 Connect 객체의 연결 정보를 가져와서 DB에 접근하므로 이 객체를 사용하기 위해서는 접속 상태인 Connection 객체가먼저 존재해야 한다. Statement 객체에서 자주 사용되는 메소드는 다음과 같다.
executeQuery(String sql) : SELECT문을 실행할 때 사용(ResultSet 객체 반환)
executeUpdate(String sql) : 삽입, 수정, 삭제와 관련된 SQL문 실행에 쓰인다.
close(): Statement 객체를 반환 할 때 쓰인다.

executeUpdate() 메소드는 삽입, 수정, 삭제와 관련된 SQL문 실행에 쓰이는데, 이것은 실행된 레코드 수를 반환해 준다.
만약 두개의 레코드를 삽입하게 되면 executeUpdate()는 int형으로 2를 반환하게 된다.
close(), Statement 객체를 모두 사용하고 메모리에 반환하기 위해 사용, 객체를 사용하지 않을 때 반환해주게 되면 메모리가 낭비되는 것을 막을 수 있다.

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
     Connection conn = null;
    Statement stmt = null;
     String sql="INSERT INTO student (num,name) VALUES (91,'홍길동')";
    
    try {
          Context init = new InitialContext();
          DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/myoracle");
          conn = ds.getConnection();
          stmt=conn.createStatement();
         
          int result=stmt.executeUpdate(sql);
          if(result!=0){
              out.println(result);
              out.println("<h3>레코드가 등록되었습니다.</h3>");
        }
    }catch(Exception e){
        out.println("<h3>레코드 등록에 실패하였습니다.</h3>");
        e.printStackTrace();
     }
%>

 

PreparedStatement
INSERT INTO student (num,name) VALUES (2,'홍길동');
INSERT INTO student (num,name) VALUES (3,'홍길동');
INSERT INTO student (num,name) VALUES (4,'홍길동');
INSERT INTO student (num,name) VALUES (5,'홍길동');
.....
만약 위 작업을 한다면 위 라인수 만큼 소스도 그만큼 방대해 질것이다.
아래 소스를 보면 stmt.setInt(1,i); 이부분이 주요한 부분이다.
sql 변수 내용을 보면 ?라는 특수문자가 포함되어 있다. 이 특수문자가 마치 함수에서의 인수와 같은 역활을 한다.
stmt.setInt(1,i); 이 코드에서 첫번째 인수가 1로 되있는 것은 SQL문의 첫번째 ? 특수문자가 오는 위치를 말한다.
두번째 인수는 ?가 있는 위치에 대치할 값을 의미 한다. (  stmt.setInt(위치,값);  )
SQL문에서 여러 조건절을 사용해서 여러 인수들을 받을 수 있다.
그 때마다 PreparedStatement의 set메소드는 여러 종류가 있다. 앞에서는 setInt()를 사용하였는데, 그이유는 넘겨주는 인자 값이 int형이기 때문
만약 넘겨주는 인자가 String 타입이라면 setString() 메소드를 사용하여 넘겨주면 된다.
즉, 넘겨주는 인자에 따라 맞는 메소드를 사용하면 되는 것이다. 그 외 기능은 Statement 객체와 거의 같다고 보면 된다.


<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
     Connection conn = null;
     String sql="INSERT INTO student (num,name) VALUES (?,'홍길동')";
    
    try {
          Context init = new InitialContext();
          DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/myoracle");
          conn = ds.getConnection();
          PreparedStatement stmt=conn.prepareStatement(sql);
         
          for(int i=110;i<120;i++){
              stmt.setInt(1,i);
              if(stmt.executeUpdate()!=0){
                  out.println("<h3>"+i+"번 레코드를 등록하였습니다.</h3>");
              }
          }
    }catch(Exception e){
        out.println("<h3>레코드 등록에 실패하였습니다.</h3>");
        e.printStackTrace();
     }
%>

 

Stored Procedure(저장된 프로시저)
데이터베이스 내에 프로시저를 선언하여 클라이언트가 필요할 대마다 호출하여 사용하는 프로시저
이것은 클라이언트에서 SQL 문을 실행하는 것과 달리 데이타베이스쪽에서 프로시저로 존재하는 것이기 때문에,
클라이언트에서 저장된 프로시저를 실행만 해주면 그 프로시저 내용이 바로 처리되므로 실행 속도 또한 더 빠르며,
부하가 적다는 장점이 있다.

CallableStatement
....
conn = ds.getConnection();
CallableStatement cs = conn.prepareCall("{call procedure_name(?,?,?)}");
cs.setInt(1,1);
cs.setString(2,"홍길동");
cs.registerOutParameter(3,java.sql.Types.VARCHAR);
cs.execute();
out.println("<h3>"+cs.getString(3)+"</h3>");
cs.close();

Stored Procedure(저장된 프로시저)를 호출하기 위해 존재하는 객체
CallableStatement 객체는 PreparedStatement 객체를 상속받아 사용하는 다.
그러므로 set메소드를 PreparedStatement 객체를 사용할 때와 똑같이 사용 가능 하다.
registerOutParameter() 메소드는 프로시저에서 넘어오는 값을 반환받기 위해서는 꼭 사용 해야 한다.
이 메소드는 프로시저로부터 넘어오는 값의 타입을 지정해주는 역활을 한다.
이와 같이 CallableStatement는 DBMS에 저장되어 있는 프로시저를 호출하기 위해 존재하는 객체로,
이것을 잘 이용한다면 더 빠른 성능의 프로그램을 만들 수 있다.

ResultSet
ResultSet이란 Statement 객체 또는 PreparedStatement 객체로 SELECT문을 사용하여 얻어온 레코드 값들을 테이블의 형태로 갖데 되는 객체
Statement 또는 PreparedStatement 객체의 executeQuery() 메소드 반환 값을 확인해보면 ResultSet로 된 것을 알 수 있다.
SELECT문을 통해서 데이터를 끌어온다면 ResultSet 객체에 그 데이터를 저장해야 한다.

clse() : ResultSet 객체를 반환
getXXX(int ColumnIndex) : 레코드 값을 지정한 XXX 타입으로 가져온다.(컬럼 인덱스 저장)
getXXX(String ColumnName) : 레코드 값을 지정한 XXX 타입으로 가져온다.(컬럼명 지정)
next() : 다음 행으로 커서를 이동한다.(다음 행이 없으면 false 반환)

getXXX() 메소드는 ResultSet 객체의 핵심 역활을 하는 메소드, ResultSet 객체는 SELECT문으로 가져온 레코드를 테이블 형태로 가지고 있으며,
그 레코드 값을 가져오기 위해서는 getXXX() 메소드를 사용한다.

아래 결과에서
rs.getInt(1)/rs.getString(2)  : 컬럼 인덱스 방법
rs.getInt("NUM")/rs.getString("NAME"): 컬럼명 지정 방법
next() : 다음 행으로 이동시키는 메소드
만약 ResultSet 객체 내에 5개의 레코드가 존재한다면, 제일 처음 next()를 실행할 때 첫번째 레코드가 있는 행으로 이동
첫번째 레코드 값을 가져오려면 이처럼 한번 행을 이동하여 getXXX() 메소드로 데이터를 가져오면 되는 것이다.
가져온 뒤 다음 레코드 값을 가져오려면 next() 메소드를 다시 호출하여 getXXX() 메소드를 사용하면 된다.
next()에서 false를 반활 할 경우 더 이상 레코드가 없는 것임을 의미 한다.

 

absolute(int row) : 지정한 위치로 커서를 이동한다.
beforeFirst() : 커서를 처음 위치로 이동한다.
afterLast() : 커서를 마지막 위치로 이동한다.
first() : 처음 레코드가 존재하는 행으로 이동한다.
last() : 마지막 레코드가 존재하는 행으로 이동한다.
next() : 다음 레코드 행으로 이동한다.
previous() : 이전 레코드 행으로 이동한다.

<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
     Connection conn = null;
     String sql="SELECT * FROM student";
    
    try {
          Context init = new InitialContext();
          DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/OracleDB");
          conn = ds.getConnection();
         
          PreparedStatement pstmt=conn.prepareStatement(sql);
          ResultSet rs=pstmt.executeQuery();
       
          while(rs.next()){
              out.println("<h3>"+rs.getInt(1)+","+rs.getString(2)+"</h3>");
              out.println("<h3>"+rs.getInt("NUM")+","+rs.getString("NAME")+"</h3>");
          }
          rs.close();
    }catch(Exception e){
        out.println("<h3>데이터 가져오기에 실패하였습니다.</h3>");
        e.printStackTrace();
     }
%>

 

- ResultSet 커서 자유롭게 움직이기

<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
     Connection conn = null;
     String sql="SELECT * FROM student";
    
    try {
          Context init = new InitialContext();
          DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/OracleDB");
          conn = ds.getConnection();
         
          PreparedStatement pstmt=
              conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,
                                           ResultSet.CONCUR_UPDATABLE);
          ResultSet rs=pstmt.executeQuery();
       
          rs.last();
          out.println(rs.getInt(1)+","+rs.getString(2)+"<br>");
          rs.first();
          out.println(rs.getInt(1)+","+rs.getString(2)+"<br>");
          rs.absolute(3);
        out.println(rs.getInt(1)+","+rs.getString(2)+"<br>");
    }catch(Exception e){
        out.println("<h3>데이터 가져오기에 실패하였습니다.</h3>");
        e.printStackTrace();
     }
%>



conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,
                                           ResultSet.CONCUR_UPDATABLE);

ResultSet 옵션 값이 붙어있는 것을 발견할 수 있다.
이 옵션이 붙어있지 않게 되면 first(), last() 등 커서 이동 관련 메소드를 사용할 수가 없다.
왜 사용할 수 없는지 확인 하기 위해 먼저 ResultSet에 존재하는 각 상수들을 보자.

TYPE_FORWARD_ONLY : 커서 이동을 다음 레코드로만 이동되도록 한다.
TYPE_SCROLL_SENSITIVE : 커서 이동을 자유롭게 하고 업데이트 내용을 반영한다.
TYPE_SCROLL_INSENSITIVE : 커서 이동을 자유롭게 하고 업데이트 내용을 반영하지 않는다.
CONCUR_UPDATEABLE : 데이터 변경이 가능하도록 한다.
CONCUR_READ_ONLY : 데이터 변경이 불가능하도록 한다.

옵션을 주지 않게 되면 기본 값으로 TYPE_FORWARD_ONLY 값 이고,
이때문에 first(), last()등 커서를 이동하는 메소드를 사용할 수 없게 되는 것이다.
즉, 스크롤이 가능하게 하는 TYPE_SCROLL_SENSITIVE, CONCUR_UPDATEABLE 옵션을 동시에 주어야
레코드의 위치를 자유롭게 이동시킬 수 있다.
레코드의 커서 이동은 자신이 원하는 데이터를 빠르게 뽑아낼 수 있어야 하기 때문에 중요하다.

ResultSetMetaData
ResultSet으로 얻어온 레코드들의 정보에 해당하는 컬럼의 정보들을 제공한다.
ResultSetMetaData 객체를 사용하게 되면, 칼럼 수나, 각 컬럼 이름, 칼럼 타입 등의 정보를 쉽게 알아낼 수 있다.

getColumnCount(): ResultSet에 저장되어있는 테이블의 칼럼의 수를 반환한다.
getColumnLabel(int column): 커서를 처음 위치로 이동한다.
getColumnName(int column): 커서를 마지막 위치로 이동한다.
getColumnType(int column): 처음 레코드가 존재하는 행으로 이동한다.
getColumnTypeName(int column): 마지막 레코드가 존재하는 행으로 이동한다.

<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
     Connection conn = null;
     String sql="SELECT * FROM student";
    
    try {
          Context init = new InitialContext();
          DataSource ds = (DataSource) init.lookup("java:comp/env/jdbc/OracleDB");
          conn = ds.getConnection();
         
          PreparedStatement pstmt = conn.prepareStatement(sql);
          ResultSet rs=pstmt.executeQuery();
        ResultSetMetaData rsmd = rs.getMetaData();

        out.println("칼럼 수 : "+rsmd.getColumnCount()+"<br>");
        for(int i=1;i<=rsmd.getColumnCount();i++){
            out.println(i+"번째 칼럼의 이름 : " + rsmd.getColumnName(i)+"<br>");
            out.println(i+"번째 칼럼의 타입 이름 : "
                 +rsmd.getColumnTypeName(i)+"<br>");
        }
    }catch(Exception e){
        e.printStackTrace();
     }
%>

 

출처 : nyhooni.tistory.com/71

반응형