[JDBC] 2021.01.05. day_49 Procedure사용 _ update, delete, insert, select

2021. 1. 5. 14:11Web_Back-end/JDBC

 

○ 주의할 점 * update, delete, select 사용하는 컬럼명과 in parameter명을 다르게 설정

※ insert

test_proc 테이블에 사원번호, 사원명, 연봉 정보를 입력
1. 프로시저 코딩
create or replace procedure insert_proc(empno in number, ename in varchar2, sal in number,
 CNT OUT NUMBER, MSG OUT VARCHAR2)

IS

   I_EMPNO NUMBER;

BEGIN

   I_EMPNO := EMPNO;

   IF EMPNO > 9999 THEN
   I_EMPNO := 0;
   END IF;

   INSERT INTO TEST_PROC (EMPNO, ENAME, SAL, HIREDATE) VALUES (I_EMPNO,ENAME,SAL,SYSDATE);

   CNT :=SQL%ROWCOUNT;

   IF CNT = 1 THEN
   COMMIT;
   MSG := '정보가 추가되었습니다.';
   END IF;

  EXCEPTION
  WHEN OTHERS THEN
  MSG :=SQLERRM || '정보가 추가되지 않았습니다.';
END;
/
2. 저장 : proc_insert.sql

3. 컴파일 : sql> @파일명.sql 
    show error : 컴파일 에러 확인
4. 실행 : 바인드 변수 선언
var cnt number;
var msg varchar2(300);
5. 직접실행 : exec, execute 를 사용
exec insert_proc(1, '김현규', 3000, :cnt, :msg);
6. 바인드 변수에 값 출력

 

※ update

test_proc 테이블에서 사원번호, 사원명, 연봉을 입력받아 해당사원의 사원명, 연봉을 변경
1. 프로시저 코딩
create or replace procedure update_proc(in_empno number, in_ename varchar2,
		in_sal number, cnt out number, msg out varchar2)

is

begin

	update	test_proc
	set		ename = in_ename, sal = in_sal
	where	empno = in_empno;

	--위의 쿼리문이 실행한 행의 수를 암시적커서(sql)의 속성(rowcount)을 사용하여 얻을 수 있다
	cnt := sql%rowcount;

	if cnt != 0 then
		msg := in_empno || '번 사원 정보가 변경되었습니다';
		commit;
	else
		msg := in_empno || '번 사원은 존재하지 않습니다';
	end if;

	exception
	when others then
	msg := sqlerrm || '문제가 발생';

end;
/
2. 저장 : proc_update.sql

3. 컴파일 : sql> @파일명.sql
    show error : 컴파일 에러 확인
select * from user_procedures;

4. 실행 : 바인드 변수 선언
var cnt number
var msg varchar2(300)

var
5. 직접실행 : exec, execute 를 사용
execute update_proc(1, '현규', 3100, :cnt, :msg);
6. 바인드 변수에 값 출력
print cnt;
print msg;

select * from test_proc;

 


※ delete

사원테이블에서 사원번호를 입력받아 해당 사원을 삭제
1. 프로시저 작성
create or replace procedure delete_proc(in_empno number,
		cnt out number, msg out varchar2)

is

begin

	delete from test_proc
	where	empno = in_empno;

	--위의 쿼리문이 실행한 행의 수를 암시적커서(sql)의 속성(rowcount)을 사용하여 얻을 수 있다
	cnt := sql%rowcount;

	if cnt = 0 then
		msg := in_empno || '번 사원은 존재하지 않습니다';
	else
		msg := in_empno || '번 사원정보가 삭제되었습니다';
	end if;

	exception
	when others then
	msg := sqlcode || '' || sqlerrm || '쿼리 실행 중 문제발생';
end;
/
2. 저장 : proc_delete.sql
3. 컴파일 : sql> @파일명.sql
4. 바인드 변수 선언
var cnt number;
var msg varchar2(1000);
5. 직접실행
exec delete_proc(1, :cnt, :msg);
6. 바인드 변수에 값 출력
print cnt;
print msg;

select * from test_proc;

 

※ Select _ 개념잡기

  procedure를 사용한 조회 
  PL/SQL 에서는 쿼리문을 사용할 수 있는데, select 은 into절이 사용되며 한행 조회만 가능하다
한 행 조회
문법 select 컬럼명,,, 
into 변수명,,
from 테이블명
...
...
한행이 아니면 예외가 발생
여러행 조회
  조회 결과가 0~n 행
cursor cursor를 사용하여 조회
PL/SQL 에서는 선언, 열기, 인출, 닫기의 생명주기를 가진 커서를 사용하여 여러 행을 조회한다
Procedure 에서는
조회 결과를 프로시저 내부에서 사용하는 것이 아닌
조회 결과를 프로시저 외부에서(예: java 에서) 사용하게 된다
커서의 제어권을SYS_REFCURSOR를 사용하여 반환해야한다
SYS_REFCURSOR를 SQLPlus에서는 REFCURSOR를 사용하여 받고
Java에서는 ResultSet을 사용하여 받는다

 

※ REFCURSOR 사용법

1. out parameter 로 커서 선언
create or replace procedure 프로시저명(변수명 out sys_refcursor)
2. 프로시저 안에서, 커서를 열고 쿼리를 실행
(모든 select 쿼리문 사용가능)
open 커서명 for select,,,,,,,,;
1. 사용 - SQLPlus에서 사용
- 바인드 변수 선언
var cur REFCURSOR

- 프로시저 호출
exec 프로시저명( :cur)

- 출력 : 조회 결과가 출력
print cur
2. Java 에서 사용

 - java.sql.Types에서 제공하는 REF_CURSOR를 사용할 수 없다
 --> oracle.jdbc.OracleTypes에서 제공하는 CURSOR를 사용해야한다

// 쿼리문 실행 객체 얻기
CallableStatement cstmt = con.prepareCall({call 프로시저명 ( ? ) });

// 바인드변수 값 할당
cstmt.registerOutParameter(1, OracleTypes.CURSOR);

// 쿼리 실행
cstmt.execute();

// out parameter에 설정된 값 받기
// ResultSet을 반환하는 method가 없기 때문에 (예: getResultSet())
// getObject() 를 casting하여 사용
ResultSet rs = (ResultSet)cstmt.getObject(1);

※ Select _ 생성해보기

1. 프로시저 작성
create or replace procedure select_proc(in_hiredate varchar2,
	cur_emp out sys_refcursor, errm out varchar2)

is

begin

	errm:= '문제가 없다';
	open cur_emp for
		select	d.deptno, d.dname, d.loc, e.empno, e.ename,
        to_char(e.hiredate, 'yyyy-mm') as hiredate
		from  	dept d, emp e
		where 	(e.deptno = d.deptno) and to_char(hiredate, 'yyyy') = in_hiredate;

	exception
	when others then
		errm := sqlerrm || '문제발생';

end;
/

 

2. 저장 : proc_select.sql
3. 컴파일 : sql> @파일명.sql
4. 바인드 변수 선언
var cur refcursor;
var msg varchar2(300);
5. 직접실행
exec select_proc('1987', :cur, :msg);

golden tool에서 : 실행하자마자 조회가 되었다

sqlplus에서는 print해야 값이 출력된다

6. 바인드 변수에 값 출력
print cur;

 



※ Java에서 procedur 실행해보기 _ CRUD 

 

empno, ename, sal, hiredate 컬럼이 있는 test_proc 테이블을 이용해서 프로시저를 실행해보자
package day0105;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

/**
 * Procedure를 사용한 CRUD
 * 
 * @author owner
 */
public class UseCallableStatementDAO {

	private static UseCallableStatementDAO ucsDAO;

	private UseCallableStatementDAO() {

	}

	public static UseCallableStatementDAO getInstance() {
		if (ucsDAO == null) {
			ucsDAO = new UseCallableStatementDAO();
		}
		return ucsDAO;
	}

	private Connection getConnection() throws SQLException {
		Connection con = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String url = "jdbc:oracle:thin:@localhost:1521:orcl";
		String id = "scott";
		String pass = "tiger";

		con = DriverManager.getConnection(url, id, pass);
		return con;
	}

	/**
	 * procedure를 사용한 insert
	 * 
	 * @throws SQLException
	 */
	public void insertTestProc() throws SQLException {
		Connection con = null;
		CallableStatement cstmt = null;
		int empno = 1111;
		String ename = "김현규";
		int sal = 2800;

		try {
			// 1. 드라이버로딩
			// 2. 커넥션 얻기
			con = getConnection();
			// 3. 쿼리문생성객체얻기
			cstmt = con.prepareCall("{call insert_proc(?, ?, ?, ?, ?)}");
			// 4. 바인드변수에 값넣기
			// in parameter : procedure에 입력하는 값 설정
			cstmt.setInt(1, empno);
			cstmt.setString(2, ename);
			cstmt.setInt(3, sal);
			// out parameter : procedure가 처리한 결과 값을 받기 위한 설정
			cstmt.registerOutParameter(4, Types.NUMERIC);
			cstmt.registerOutParameter(5, Types.VARCHAR);
			// 5. 쿼리 실행 : procedure 호출
			cstmt.execute();
			// 6. out parameter에 설정된 값 받기
			int cnt = cstmt.getInt(4);
			String msg = cstmt.getString(5);
			System.out.println(cnt);
			System.out.println(msg);
		} finally {
			// 7. 연결끊기
			if (cstmt != null) {
				cstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}

	}

	/**
	 * procedure를 사용한 update
	 * 
	 * @return update 한 횟수
	 * @throws SQLException
	 */
	public int updateTestProc() throws SQLException {
		int cnt = 0;

		Connection con = null;
		CallableStatement cstmt = null;

		int empno = 1111;
		String ename = "현규";
		int sal = 3000;

		try {
			// 1. 드라이버로딩
			// 2. 커넥션얻기
			con = getConnection();
			// 3. 쿼리문생성객체얻기
			cstmt = con.prepareCall("{call update_proc(?, ?, ?, ?, ?)}");

			// 4. 바인드변수에 값넣기
			// in parameter
			// in_empno, in_ename, in_sal
			// : select, update, delete에는 컬럼명과 in parameter명을 다르게 설정해야한다
			cstmt.setInt(1, empno);
			cstmt.setString(2, ename);
			cstmt.setInt(3, sal);

			// out parameter
			// cnt, msg
			cstmt.registerOutParameter(4, Types.NUMERIC);
			cstmt.registerOutParameter(5, Types.VARCHAR);

			// 5. 쿼리 실행 : procedure 호출
			cstmt.execute();
			// 6. out parameter에 설정된 값 받기
			cnt = cstmt.getInt(4);
			String msg = cstmt.getString(5);
			System.out.println(cnt + " / " + msg);
		} finally {
			// 7. 연결끊기
			if (cstmt != null) {
				cstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}

		return cnt;
	}

	/**
	 * procedure를 사용한 delete
	 * 
	 * @return 삭제한 횟수
	 * @throws SQLException
	 */
	public int deleteTestProc() throws SQLException {
		int cnt = 0;

		Connection con = null;
		CallableStatement cstmt = null;

		int empno = 1111;

		try {
			// 1. 드라이버로딩
			// 2. 커넥션얻기
			con = getConnection();
			// 3. 쿼리문생성객체얻기
			cstmt = con.prepareCall("{call delete_proc(?, ?, ?)}");

			// 4. 바인드변수에 값넣기
			// in parameter
			// in_empno
			// : select, update, delete에는 컬럼명과 in parameter명을 다르게 설정해야한다
			cstmt.setInt(1, empno);

			// out parameter
			// cnt, msg
			cstmt.registerOutParameter(2, Types.NUMERIC);
			cstmt.registerOutParameter(3, Types.VARCHAR);

			// 5. 쿼리 실행 : procedure 호출
			cstmt.execute();
			// 6. out parameter에 설정된 값 받기
			cnt = cstmt.getInt(2);
			String msg = cstmt.getString(3);
			System.out.println(cnt + " / " + msg);

		} finally {
			// 7. 연결끊기
			if (cstmt != null) {
				cstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}

		return cnt;
	}

	public static void main(String[] args) {

		UseCallableStatementDAO ucsDAO = UseCallableStatementDAO.getInstance();
		try {

			// 1. insert
//			ucsDAO.insertTestProc();

			// 2. update
//			int cnt = ucsDAO.updateTestProc();
//			if(cnt != 0) {
//				System.out.println("사원정보 변경 성공");
//			}else {
//				System.out.println("사원번호를 확인하세요");
//			}

			// 3. delete
			int cnt = ucsDAO.deleteTestProc();
			if (cnt != 0) {
				System.out.println("사원정보 삭제 성공");
			} else {
				System.out.println("삭제할 사원번호를 확인하세요");
			}

		} catch (SQLException e) {
			System.out.println("쿼리문 실행 중 문제발생");
			e.printStackTrace();
		}

	}

}

 


▽▼ VO 클래스 ▼▽

package day0105;

public class ProcedureVO {

	private int deptno, empno, sal;
	private String dname, loc, ename, hiredate;

	public ProcedureVO() {
		super();
	}

	public ProcedureVO(int deptno, int empno, int sal, String dname, String loc, String ename, String hiredate) {
		super();
		this.deptno = deptno;
		this.empno = empno;
		this.sal = sal;
		this.dname = dname;
		this.loc = loc;
		this.ename = ename;
		this.hiredate = hiredate;
	}

	public int getDeptno() {
		return deptno;
	}

	public void setDeptno(int deptno) {
		this.deptno = deptno;
	}

	public int getEmpno() {
		return empno;
	}

	public void setEmpno(int empno) {
		this.empno = empno;
	}

	public int getSal() {
		return sal;
	}

	public void setSal(int sal) {
		this.sal = sal;
	}

	public String getDname() {
		return dname;
	}

	public void setDname(String dname) {
		this.dname = dname;
	}

	public String getLoc() {
		return loc;
	}

	public void setLoc(String loc) {
		this.loc = loc;
	}

	public String getEname() {
		return ename;
	}

	public void setEname(String ename) {
		this.ename = ename;
	}

	public String getHiredate() {
		return hiredate;
	}

	public void setHiredate(String hiredate) {
		this.hiredate = hiredate;
	}

	@Override
	public String toString() {
		return "ProcedureVO [deptno=" + deptno + ", empno=" + empno + ", sal=" + sal + ", dname=" + dname + ", loc="
				+ loc + ", ename=" + ename + ", hiredate=" + hiredate + "]";
	}

}

▼ 실행 클래스

package day0105;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

/**
 * Procedure를 사용한 CRUD
 * 
 * @author owner
 */
public class UseCallableStatementDAO {

	private static UseCallableStatementDAO ucsDAO;

	private UseCallableStatementDAO() {

	}

	public static UseCallableStatementDAO getInstance() {
		if (ucsDAO == null) {
			ucsDAO = new UseCallableStatementDAO();
		}
		return ucsDAO;
	}

	private Connection getConnection() throws SQLException {
		Connection con = null;
		try {
			Class.forName("oracle.jdbc.OracleDriver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String url = "jdbc:oracle:thin:@localhost:1521:orcl";
		String id = "scott";
		String pass = "tiger";

		con = DriverManager.getConnection(url, id, pass);
		return con;
	}
    
	public List<ProcedureVO> selectProcedure(String hiredate) throws SQLException {
		List<ProcedureVO> list = new ArrayList<ProcedureVO>();

		Connection con = null;
		CallableStatement cstmt = null;
		ResultSet rs = null;

		try {
			// 1. 드라이버로딩
			// 2. 커넥션얻기
			con = getConnection();
			// 3. 쿼리문생성객체얻기
			cstmt = con.prepareCall("{call select_proc(?, ?, ?)}");
			// 4. 바인드변수에 값넣기
			// in parameter
			cstmt.setString(1, hiredate);

			// out parameter
//			cstmt.registerOutParameter(2, Types.REF_CURSOR); // Java에서 제공하는 커서 ==> oracle에서는 사용할 수 없다
			// SYS_REFCURSOR를 받기 위해 OracleTypes.CURSOR를 사용한다
			cstmt.registerOutParameter(2, OracleTypes.CURSOR);
			cstmt.registerOutParameter(3, Types.VARCHAR);

			// 5. 쿼리 실행 : procedure 호출
			cstmt.execute();
			// 6. out parameter에 설정된 값 받기
			rs = (ResultSet) cstmt.getObject(2);
			String msg = cstmt.getString(3);
			System.out.println(msg);

			ProcedureVO pVO = null;
			while (rs.next()) {// ResultSet 이 cursor의 제어권을 받는다
				pVO = new ProcedureVO(rs.getInt("deptno"), rs.getInt("empno"),
						rs.getString("dname"), rs.getString("loc"), 
						rs.getString("ename"), rs.getString("hiredate"));
				
				list.add(pVO);			
			}

		} finally {
			// 7. 연결끊기
			if (rs != null) {
				rs.close();
			}
			if (cstmt != null) {
				cstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}

		return list;
	}

	public static void main(String[] args) {

		UseCallableStatementDAO ucsDAO = UseCallableStatementDAO.getInstance();
		try {
			String hiredate ="1981";
			List<ProcedureVO> list = ucsDAO.selectProcedure(hiredate);
			System.out.println(hiredate + "년도 조회");
			if(list.isEmpty()) {
				System.out.println("해당 년도에 입사한 사원이 없습니다");
			}else {
				for(ProcedureVO pVO: list) {
					System.out.println(pVO.getDeptno() + " / " + pVO.getDname() + 
                    " / " + pVO.getLoc() + " / " + pVO.getEmpno() + " / " + pVO.getEname() +
                    " / " + pVO.getHiredate());
				}
			}

		} catch (SQLException e) {
			System.out.println("쿼리문 실행 중 문제발생");
			e.printStackTrace();
		}

	}

}

REF_CURSOR : oracle에서는 사용할 수 없음을 알 수 있다
조건에 맞는 레코드값이 select 되었음을 알 수 있다