ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 008. CallableStatement
    SsY/Class 2023. 5. 2. 14:16
    728x90
    JDBC
    - CallableStatement 설명
    • CallableStatement
    ■■■ CallableStatement ■■■
    // 호출이 가능한 작업객체~ 개발자로서 많이 사용되지는 않음
    // PreparedStatement 를 가장 많이 활용하게 됨 -- 왜냐면 사용하는 것 만으로도 Secure Coding 가능
    -----------------------------------------------------------------------------------------------------------
    1. CallableStatement 인터페이스는 모든 DBMS에 대한
       표준 방법으로 저장 프로시저(stored procedure) 를 호출하는 방법을 제공하는 인터페이스이며,
       호출은 escape 문법으로 작성된다.
    -----------------------------------------------------------------------------------------------------------
    2. 결과 매개변수를 가지는 형태와 결과 매개변수가 없는 형태(두 가지)이며
       OUT 매개변수의 종류인 결과 매개변수는
       저장 프로시저에 대한 반환값이다.
       // ▼ 핵심 ▼
       두 형태 모두 IN 매개변수(입력), OUT 매개변수(출력), INOUT 매개변수(입출력)
       를 위해 사용되는 매개변수의 변수 번호를 가지며
       『?』는 매개변수의 저장 위치로 나타낸다.
    -----------------------------------------------------------------------------------------------------------
    3. 형식 및 구조
       (JDBC 에서 저장 프로시저 호출을 위한 문법)
       // 쿼리문을 직접 운용할 필요가 없음 -> 함수나 프로시저가 처리
    
       매개변수가 없는 저장 프로시저 호출
       {call procedure_name}
       IN 매개변수를 넘겨주는 프로시저 호출
       {call procedure_name[(?, ?, ...)]}
       OUT 매개변수를 반환하는 프로시저 호출 / 반환자료형이 존재하는 함수 호출
       {? = call procedure_name[(?, ?, ...)]}
    -----------------------------------------------------------------------------------------------------------
    4. CallableStatement 객체 생성
       CallableStatement 객체는
       Connection 의 prepareCall() 메소드에 의해 생성된다.
       예를 들어, 두 개의 인자값을 가지고 있으며 결과 매개변수가 없는
       저장 프로시저 preData 를 호출하는 방법은 다음과 같다.
    
       CallableStatement cstmt = conn.prepareCall("{call preData(?, ?)}");
    
       ※ 물음표(『?』)의 위치가 IN, OUT, INOUT  매개변수 중
          어떤 것인가 하는 것은 저장 프로시저 prcData 에 의존한다.
    -----------------------------------------------------------------------------------------------------------
    5. IN, OUT 매개변수
       // 매개변수의 정의는 DB 상에서 규정된 것을 사용 -> 이 때문에 사용빈도가 낮음
       CallableStatement 객체로 IN 매개변수를 넘겨주는 것은
       PreparedStatement 로 부터 상속받은 『setXxx()』 메소드이다.
       예를 들어, setFloat() 메소드는 float 값을 넘겨주게 되는 것이다.
    
       만약, 저장 프로시저가 OUT 매개변수를 반환한다면
       각 OUT 매개변수의 SQL 형(Type)은 CallblesStatement 객체를
       실행할 수 있는 상태가 되기 전까지 등록되어져야 한다.
       이는 몇 몇 DBMS 들이 SQL형(Type)을 필요로 하기 때문이며,
       이 때, SQL 형(Type) 을 등록하는 것은 registerOutPatrameter() 메소드를 사용하게 된다.
       그리고, SQL 구문이 실행되어진 다음에
       CallableStatement 의 『getXxx()』 메소드는 매개변수의 값을 검색하며,
       사용되는 정확한 『getXxx()』 메소드는 그 매개변수를 위해 등록된
       SQL 에 상응하는 자바 형(Type)이다.
       즉, registerOutParameter() 메소드는 SQL 타입을 사용하고, 
       getXxx() 메소드는 이것을 자바 타입으로 캐스트(cast) 하는 것이다.

     

    close project / 열 때는 openProject 사용하면 됨


    JDBC08

    • DBConn 생성
    더보기
    /*=================
    	DBConn.java
    	- throws 
    ===================*/
    
    package com.util;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class DBConn
    {
    	private static Connection dbConn;
    	
    	public static Connection getConnection() throws ClassNotFoundException, SQLException
    	{
    		if (dbConn == null)
    		{
    			String url = "jdbc:oracle:thin:@localhost:1521:xe";
    			String user = "scott";
    			String pwd = "user";
    		
    			Class.forName("oracle.jdbc.driver.OracleManager");
    			dbConn = DriverManager.getConnection(url,user,pwd);
    		}
    		
    		return dbConn;
    	}
    	
    	public static Connection getConnection(String url, String user, String pwd) throws ClassNotFoundException, SQLException
    	{
    		if (dbConn == null)
    		{
    			Class.forName("oracle.jdbc.driver.OracleManager");
    			dbConn = DriverManager.getConnection(url, user, pwd);
    		}
    
    		return dbConn;
    	}
    	
    	public static void Close() throws SQLException
    	{
    		if (dbConn != null)
    		{
    			if (!dbConn.isClosed())
    			{
    				dbConn.close();
    			}
    		}
    		dbConn = null;
    	}
    }

    SQL DEVELOPER

    더보기
    SELECT USER
    FROM DUAL;
    --==>> SCOTT
    
    SELECT *
    FROM TBL_MEMBER
    ORDER BY SID;
    --==>> 
    /*
    1	별희강	010-1111-1111
    2	수인주	010-2222-2222
    3	기배리	010-3333-3333
    4	하경최	010-4444-4444
    5	희정권	010-5555-5555
    6	이서최	010-6666-6666
    7	상훈문	010-7777-7777
    8	준복이	010-8888-8888
    */
    
    TRUNCATE TABLE TBL_MEMBER;
    --==>> Table TBL_MEMBER이(가) 잘렸습니다.
    
    SELECT *
    FROM TBL_MEMBER
    ORDER BY SID;
    --==>> 조회결과 없음
    
    --○ CallableStatement 실습을 위한 프로시저 작성
    -- 프로시저 명   : PRC_MEMBERINSERT
    -- 프로시저 기능 : TBL_MEMBER 테이블에 데이터를 입력하는 입력 프로시저
    --// 매개변수 이름, 전화번호 받아오면 SID 를 MAX() + 1 로 구성하는 프로시저
    CREATE OR REPLACE PROCEDURE PRC_MEMBERINSERT
    (   V_NAME    IN  TBL_MEMBER.NAME%TYPE
    ,   V_TEL     IN  TBL_MEMBER.TEL%TYPE
    )
    IS
        -- 주요 변수 선언
        V_SID TBL_MEMBER.SID%TYPE;
    BEGIN
        -- 기존 SID 의 최대값 얻어오기   
        SELECT NVL(MAX(SID),0) INTO V_SID
        FROM TBL_MEMBER;
    
        -- INSERT 구문 
        INSERT INTO TBL_MEMBER(SID, NAME, TEL) VALUES((V_SID+1), V_NAME, V_TEL);
    
        -- 커밋
        COMMIT;
    END;
    --==>> Procedure PRC_MEMBERINSERT이(가) 컴파일되었습니다.
    
    --○ 생성된 프로시저 테스트(정상 작동여부 확인)
    EXEC PRC_MEMBERINSERT('건우박','010-1111-1111');
    --==>> PL/SQL 프로시저가 성공적으로 완료되었습니다.
    
    --○ 테이블 조회
    SELECT *
    FROM TBL_MEMBER
    ORDER BY SID;
    --==>> 1	건우박	010-1111-1111

    • Test001
      - DML 쿼리문
    /*=======================================================
    	Test001.java
    	- CallableStatement 를 활용한 SQL 구문 전송 실습 
    =========================================================*/
    
    package com.test;
    
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.util.Scanner;
    
    import com.util.DBConn;
    
    public class Test001
    {
    	public static void main(String[] args)
    	{
    		try
    		{
    			Scanner sc = new Scanner(System.in);
    			Connection conn = DBConn.getConnection();
    		
    			do
    			{
    				System.out.print("이름 입력(-1 종료) : ");
    				String name = sc.next();
    				
    				if (name.equals("-1"))
    					break;
    				
    				System.out.print("전화번호 입력 : ");
    				String tel = sc.next();
    				
    				if (conn != null)
    				{
    					System.out.println("데이터베이스 연결 성공~");
    					
    					try
    					{
    						// 쿼리문 준비 check
    						//-- PreparedStatement를 상속받아 쿼리문을 먼저 준비해야함
    						String sql = "{call PRC_MEMBERINSERT(?,?)}";
    						
    						// CallableStatement 작업 객체 생성 check
    						CallableStatement cstmt = conn.prepareCall(sql);
    						
    						// 매개변수 전달
    						cstmt.setString(1, name);
    						cstmt.setString(2, tel);
    						
    						//-- Statement들은 executeUpdate, executeQuery 를 사용하는 기준은 동일하다.
    						int result = cstmt.executeUpdate();
    						
    						if (result > 0)
    							System.out.println("프로시저 호출 및 데이터 입력 완료~");
    						
    					} catch (Exception e)
    					{
    						System.out.println(e.toString());
    					}
    				}
    			} while (true);
    			
    			DBConn.Close();
    			
    			System.out.println("\n데이터베이스 연결 종료...");
    			System.out.println("프로그램 종료됨...");
    			
    		} catch (Exception e)
    		{
    			System.out.println(e.toString());
    		}
    	}
    }

    SQL DEVELOPER

    --○ JDBC 의 Test001 실행 후 확인
    SELECT *
    FROM TBL_MEMBER
    ORDER BY SID;
    --==>>
    /*
    1	건우박	010-1111-1111
    2	연수양	010-2222-2222
    3	지혜김	010-3333-3333
    4	아현한	010-4444-4444
    */

    SQL DEVELOPER

    --○ CallableStatement 실습을 위한 프로시저 작성
    -- 프로시저 명   : PRC_MEMBERSELECT
    -- 프로시저 기능 : TBL_MEMBER 테이블에 데이터를 읽어오는 입력 프로시저
    -- ※ 『SYS_REFCURSOR』 자료형을 이용하여 커서 다루기
    --// 오라클에서 기본저긍로 제공하는 커서
    CREATE OR REPLACE PROCEDURE PRC_MEMBERSELECT
    (   VRESULT OUT SYS_REFCURSOR
    )
    IS
        -- 주요 변수 선언 → 커서 정의(X)
    BEGIN
        OPEN VRESULT FOR            --FOR 은 각각에 집어넣을 때
            SELECT SID, NAME, TEL   --┐
            FROM TBL_MEMBER         --┃-- 이 구문 안에 집어넣는다는 뜻
            ORDER BY SID;           --┘
        --// ※ 외부에서 쓰려고 만든 커서이기 때문에 닫으면 안됨
        --CLOSE VRESULT;
        
        --// ※ DML 구문이 아니기 때문에 사용하지 않는다.
        --COMMIT;
    END;
    --> Procedure PRC_MEMBERSELECT이(가) 컴파일되었습니다.

    • Test002
      - Select 쿼리문
    /*=======================================================
    	Test002.java
    	- CallableStatement 를 활용한 SQL 구문 전송 실습 
    =========================================================*/
    
    package com.test;
    
    import java.sql.CallableStatement;
    import java.sql.Connection;
    
    import com.util.DBConn;
    
    import oracle.jdbc.internal.OracleTypes;
    
    public class Test002
    {
    	public static void main(String[] args)
    	{
    		try
    		{
    			Connection conn = DBConn.getConnection();
    			
    			if (conn != null)
    			{
    				System.out.println("데이터베이스 연결 성공~");
    				
    				try
    				{
    					// 쿼리문 준비 check
    					String sql = "{call PRC_MEMBERSELECT(?)}";
    					
    					// CallableStatement 작업 객체 생성 check
    					CallableStatement cstmt = conn.prepareCall(sql);
    					
    					// chekck
    					// 프로시저 내부에서 SYS_REFCURSOR 를 사용하고 있기 때문에
    					// OracleTypes.CURSOR 를 사용하기 위한 등록 과정이 필요한 상황
    					// 1. Project Explorer 상에서 해당 프로젝트 마우스 우클릭
    					//  > Build Path > Configure Build Path 클릭
    					// 2. 이후 열리는 대화창에서 Libraries 탭 클릭
    					//  > 우측 Add External Jars 버튼 클릭
    					// 3. 이 때 열리는 파일 등록창에
    					//  『ojdbc6.jar』 파일 추가 등록
    					// 4. 『import oracle.jdbc.internal.OracleTypes;』 구문 추가 등록
    					
    					/*
    					//--※ Test001 과 비교
    					Test001
    					get 이나 set 은 자바에서 사용 -- 내부적으로 오라클에 값을 전달할 때,
    					오라클은 자료구조를 매핑하는 테이블이 있어 String -> VARCHAR2 로 변환해서 가져옴
    					그러나
    					Test002
    					커서타입으로 넘기는건 오라클에서 제대로확인이 불가능하기 때문에 자바에서 오라클 내부의 타입을 지정해서 건네줌
    					*/
    					
    					// 매개변수 전달 check
    					// cstmt.registerOutParameter(1, 오라클에서의커서);
    					cstmt.registerOutParameter(1, OracleTypes.CURSOR);
    
    
    				} catch (Exception e)
    				{
    					System.out.println(e.toString());
    				}
    			}
    		} catch (Exception e)
    		{
    			System.out.println(e.toString());
    		}
    	}
    }
    더보기

    ※ Build Path 설정
    - Oracle CURSOR 사용을 위해서


    왜 registerOutParameter() 을 사용하는지 설명

     

    /*=======================================================
    	Test002.java
    	- CallableStatement 를 활용한 SQL 구문 전송 실습 
    =========================================================*/
    
    package com.test;
    
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import com.util.DBConn;
    
    import oracle.jdbc.internal.OracleTypes;
    
    public class Test002
    {
    	public static void main(String[] args) throws SQLException
    	{
    		try
    		{
    			Connection conn = DBConn.getConnection();
    			
    			if (conn != null)
    			{
    				System.out.println("데이터베이스 연결 성공~");
    				
    				try
    				{
    					// 쿼리문 준비 check
    					String sql = "{call PRC_MEMBERSELECT(?)}";
    					
    					// CallableStatement 작업 객체 생성 check
    					CallableStatement cstmt = conn.prepareCall(sql);
    					
    					// check
    					// 프로시저 내부에서 SYS_REFCURSOR 를 사용하고 있기 때문에
    					// OracleTypes.CURSOR 를 사용하기 위한 등록 과정이 필요한 상황
    					// 1. Project Explorer 상에서 해당 프로젝트 마우스 우클릭
    					//  > Build Path > Configure Build Path 클릭
    					// 2. 이후 열리는 대화창에서 Libraries 탭 클릭
    					//  > 우측 Add External Jars 버튼 클릭
    					// 3. 이 때 열리는 파일 등록창에
    					//  『ojdbc6.jar』 파일 추가 등록
    					// 4. 『import oracle.jdbc.internal.OracleTypes;』 구문 추가 등록
    					
    					/*
    					//--※ Test001 과 비교
    					Test001
    					get 이나 set 은 자바에서 사용 -- 내부적으로 오라클에 값을 전달할 때,
    					오라클은 자료구조를 매핑하는 테이블이 있어 String -> VARCHAR2 로 변환해서 가져옴
    					그러나
    					Test002
    					커서타입으로 넘기는건 오라클에서 제대로확인이 불가능하기 때문에 자바에서 오라클 내부의 타입을 지정해서 건네줌
    					*/
    					
    					// 매개변수 전달 check
    					// cstmt.registerOutParameter(1, 오라클에서의커서);
    					cstmt.registerOutParameter(1, OracleTypes.CURSOR);
    					// 주고 받을 때 
    					cstmt.execute();
    
    					// + 수신하는것이 문자 형태로 넘겨 받는것이 아닌 커서라는 객체 자체를 받아오는 것
    					// 첫번째 파라미터 값으로 넘겨받겠다.
    					// ResultSet 타입으로 받아야 처리 할 수 있는데 가져오는 것은 Object 타입
    					// 다운 캐스팅!
    					//ResultSet rs = cstmt.getObject(1);
    					ResultSet rs = (ResultSet)cstmt.getObject(1);
    					
    					while (rs.next())
    					{
    						String sid = rs.getString("SID");
    						String name = rs.getString("NAME");
    						String tel = rs.getString("TEL");
    						
    						/*
    						String str = String.format("%3s %7s %12s", sid, name, tel);
    						System.out.println(str);
    						*/
    						// 위와 동일한 구문
    						System.out.printf("%3s %7s %12s", sid, name, tel);
    					}
    					rs.close();
    					cstmt.close();
    					
    				} catch (Exception e)
    				{
    					System.out.println(e.toString());
    				}
    			}
    		} catch (Exception e)
    		{
    			System.out.println(e.toString());
    		}
    		
    		DBConn.Close();
    		
    		System.out.println("\n데이터베이스 연결 닫힘...");
    		System.out.println("프로그램 종료됨");
    	}
    }
    728x90
Designed by planet-si