강좌
클라우드/리눅스에 관한 강좌입니다.
데이터베이스 분류

스토어드 프로시저/함수를 자바로 작성하는 법

작성자 정보

  • 웹관리자 작성
  • 작성일

컨텐츠 정보

본문

스토어드 프로시저/함수를 자바로 작성하는 법

 

t.gif
t.gif
OS: Microsoft Windows 2000 [Version 5.00.2195]
DBMS: Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
작성자: 강명규
작성일: 2004-04-17

이 글은 Oracle and Java Stored Porcedures(www.developer.com/db/article.php/3337411)을 정리한 것임.
이 글에 나오는 예제또한 약간의 첨삭이 있지만, 원문에 충실히 할 것임.

오라클에서 stored procedure/function은 전통적으로 PL/SQL로 작성했었다.
사실, Native언어(C언어등 OS상에서 실행파일을 얻는)나 자바로도 가능하다.
특이하게 자바의 경우 오라클에서 VM을 제공하므로 별다른 복잡한 절자없이
PL/SQL처럼 손쉽게 스토어드 프로시저나 함수를 생성할 수 있다.

그럼, 왜 PL/SQL이 있는데 또 다른 언어를 지원하려는 것일까?
네이티브코드는 성능에 잇점이 있을 수 있다.
몇몇 글을 보면 네이티브코드의 경우, 오라클에 존재하는 PL/SQL로 작성된 패키지들을 전체를
네이티브코드로 변경해야 성능상의 이점을 볼 수 있다고 한다. PL/SQL과 섞어쓰면 성능이
오히려 떨어진다고 한다.

그럼 자바는?
실체 성능적인 부분을 측정해보지는 않았는데 PL/SQL과 혼합해서 써도 무방하다고 한다.

자바를 사용하면 좋은 점은?
어느 정도 데이터베이스와 독립적
PL/SQL을 별도로 배울 필요가 없음
개발팀에서 PL/SQL보다는 자바를 더 능숙하게 다루는 팀원이 많을 경우가 대개일 것임.

자바를 사용하면 찝찝한 점은?
성능이 아무래도 pl/sql로 작성한 것보다는 떨어진다.
오라클 SGA에 자바를 위한 메모리를 할당해줘야 한다.
즉, 성능과 리소스를 더 필요로 한다는 의미.

필자의 생각은?
PL/SQL로 작성하던 놈이라 별다른 것이 없다면 PL/SQL로 작성하고,
PL/SQL로 작성하기 어려우면 JAVA를 고려하는 것이 좋겠다.


들리는 소문에 의하면, 차후 오라클버전에서 PL/SQL을 없애고 자바로 대체한다는
믿거나 말거나 하는 얘기가 떠돈다. 사실 오라클사에서는 현재 PL/SQL은 현상유지 차원에서
계속 지원할 것이라고 하나, 자바쪽으로 비중이 옮겨가는 것은 사실인 것 같다.

결론은?
오라클 8i부터 자바로 stored procedure/function를 사용할 수 있다.
원래 pl/sql을 사용하던 사람은 계속 pl/sql을 쓰고, 처음하는 사람은 자바를 사용하도록 하자.
아무래도 둘이 섞어쓰면 찜찜할 것 같긴하다.
실제 자바소스가 변경되어, 오라클로 reload한후, 호출명세를 다시 설정해주지 않았더니 오라클이 맛가는 현상을 겪었다.
alter system kill session '';으로 어떻게 해보려고 했으나 감감 무소식.. shutdown abort밖에 없었다.


자, 이제 직관적으로 간단명료하게 진행하겠다.
우선 오라클에 테스트용 테이블을 생성한다.
SQL> create table test(id int, name varchar(10), salary float);

테이블이 생성되었습니다.

자바로 로직을 작성하고, loadjava를 이용하여 DB에 집어넣는다.
윈도   c:oracleora92inloadjava.bat
유닉스
[sky@gw1 sky]$ file $ORACLE_HOME/bin/loadjava
/u01/app/oracle/product/9.2.0/bin/loadjava: Bourne shell script text executable


[develope/load class]
D: emp>type TestDML.java
import java.sql.*;
import oracle.jdbc.*;

public class TestDML
{
        // 리턴값이 없으면 오라클 Stored Procedure
        public static void addPerson(int id, String name, float salary)
        {
                System.out.println("새로운 사용자 등록");

                try
                {
                        Connection conn = DriverManager.getConnection("jdbc:default:connection:");
                        String sql = "insert into test values (?,?,?)";
                        PreparedStatement pstmt = conn.prepareStatement(sql);
                        pstmt.setInt(1,id);
                        pstmt.setString(2,name);
                        pstmt.setFloat(3,salary);
                        pstmt.executeUpdate();
                        pstmt.close();
                }
                catch(SQLException e)
                {
                        System.err.println("ERROR! Adding Employee: " + e.getMessage());
                }
        }

        // 리턴값이 있으면 오라클 Function
        public static float getSalary(int id)
        {
                float salary = 0;
                System.out.println(id + "의 급여");

                try
                {
                        ResultSet rs;
                        Connection conn = DriverManager.getConnection("jdbc:default:connection:");
                        String sql = "select salary from test where id=?";
                        PreparedStatement pstmt = conn.prepareStatement(sql);
                        pstmt.setInt(1,id);
                        rs = pstmt.executeQuery();

                        rs.next();
                        salary = rs.getFloat(1);

                        rs.close();
                        pstmt.close();
                }
                catch(SQLException e)
                {
                        System.err.println("ERROR! Adding Employee: " + e.getMessage());
                }

                return salary;
        }
}

-u : DB계정
-v : verbose feedback
-resolve : .class파일이 아닌 .java파일이므로 컴파일해주라.
D: emp>loadjava -u kang/xxxxxx -v -resolve TestDML.java
arguments: '-u' 'kang/xxxxxx' '-v' '-resolve' 'TestDML.java'
created  : JAVA$CLASS$MD5$TABLE
creating : source TestDML
created  : CREATE$JAVA$LOB$TABLE
loading  : source TestDML
creating : TestDML
resolving: source TestDML

오라클 kang계정을 보면, 뭔가 생성된 것이 보일 것이다.
SQL> select * from tab;

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
CREATE$JAVA$LOB$TABLE          TABLE
JAVA$CLASS$MD5$TABLE           TABLE
JAVA$OPTIONS                   TABLE
TEST                           TABLE

SQL> select object_name, object_type, status from user_objects
  2  where object_name='TestDML';

OBJECT_NAME     OBJECT_TYPE        STATUS
--------------- ------------------ -------
TestDML         JAVA CLASS         VALID
TestDML         JAVA SOURCE        VALID

SQL>

위에서 status필드가 VALID가 아니라면 user_errors를 조회해보라.
loadjava는 .sqlj .properties .ser .jar .zip을 인식한다.
만일, kang계정이외에 maddog계정도 이 놈을 사용하겠다고 하면 다음과 같이 한다.

D: emp>loadjava -u kang/xxxxxx -resolve -resolver "((* KANG)(* MADDOG)(* PUBLIC))" TestDML.java



[publising classes]
SQL이나 PL/SQL에서 자바로 생성한 클래스파일을 호출하려면 publish를 해야 하는데,
자바 클래스는 호출명세(call specification, call spec 혹은 PL/SQL wrapper이라고 불리기도 함)
를 생성/컴파일함으로써 publish될 수 있다.
호출명세는 자바 메소드의 인자와 리턴형을 오라클 SQL타입과 매핑하는 역활을 한다.

SQL> create or replace procedure add_person(id number, name varchar2, salary number)
  2  as language java
  3  name 'TestDML.addPerson(int, java.lang.String, float)';
  4  /

프로시저가 생성되었습니다.

SQL> create or replace function get_salary(id number) return number
  2  as language java
  3  name 'TestDML.getSalary(int) return int';
  4  /

함수가 생성되었습니다.


즉, 우리는 이제부터 add_person프로시저와 get_salary함수를 사용하면 되는 것이다.
자바출력은 트레이스파일로 쓰여지므로, 출력을 화면에 표시하도록 변경후 프로시저 호출
SQL> set serveroutput on
SQL> call dbms_java.set_output(2000);

호출이 완료되었습니다.

SQL> exec add_person(1,'강명규', 3000.01);
새로운 사용자 등록

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> select get_salary(1) from dual;

GET_SALARY(1)
-------------
      3000.01

함수는 다음과 같이 해도 된다.
SQL> var salary number;
SQL> call get_salary(1) into :salary;
1의 급여

호출이 완료되었습니다.

SQL> print salary;

    SALARY
----------
   3000.01


자바에서 PL/SQL을 호출하는 방법
CallableStatement cstmt = conn.prepareCall("{my_plsql_porc}");



[Usage Scenario]
오라클에서 OS상의 파일에 대한 작업을 하려면 UTL_FILE패키지를 이용해야 한다.
이것은 OS상의 파일에 접근할 수 있는 인터페이스를 제공하지만 여러가지로 불편하다.
이 경우 자바의 File I/O를 이용하면 더 효율적으로 작업할 수 있을 것이다.


D: emp>type FileTest.java
import java.io.*;

class FileTest
{
        public static String readFile(String usrFile)
        {
                String fileStr = new String();
                String tmpStr = new String();
                try
                {
                        File file = new File(usrFile);
                        FileReader fr = new FileReader(usrFile);
                        LineNumberReader lnr = new LineNumberReader(fr);

                        do{
                                tmpStr = lnr.readLine();
                                fileStr += tmpStr;
                        } while(tmpStr != null);

                        lnr.close();
                        fr.close();

                }
                catch(Exception e)
                {
                        System.err.println("ERROR! Handling File: " + e.getMessage());
                }

                return fileStr;
        }
}


D: emp>loadjava -u kang/xxxxxx -v -resolve FileTest.java
arguments: '-u' 'kang/xxxxxx' '-v' '-resolve' 'FileTest.java'
creating : source FileTest
loading  : source FileTest
creating : FileTest
resolving: source FileTest


SQL> create or replace package my_java_utils is
  2  function read_file(file varchar2) return varchar2;
  3  end my_java_utils
  4  ;
  5  /

패키지가 생성되었습니다.

SQL> create or replace package body my_java_utils is
  2  function read_file(file varchar2) return varchar2
  3  as language java
  4  name 'FileTest.readFile(java.lang.String) return java.lang.String';
  5  end my_java_utils;
  6  /

패키지 본문이 생성되었습니다.

SQL>


D: emp>type hello
안녕!
나는 강명규라고 해!


SQL> conn system/xxxxxx
연결되었습니다.
SQL> exec dbms_java.grant_permission( 'KANG', 'SYS:java.io.FilePermission','d: emphello', 'read' );

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> conn kang/xxxxxx
연결되었습니다.
SQL> set serveroutput on
SQL> call dbms_java.set_output(2000);

호출이 완료되었습니다.
SQL> select my_java_utils.read_file('d: emphello') from dual;

MY_JAVA_UTILS.READ_FILE('D:TEMPHELLO')
--------------------------------------------------------------------------------
안녕!나는 강명규라고 해!null

SQL>

사실 위에서는 hello라는 파일로 했지만, alert.log파일에 대한 처리로 하면 좀 더 현실적일 것이다.


[작업한 내용 제거]
SQL> conn system/xxxxxx
SQL> exec dbms_java.revoke_permission( 'KANG', 'SYS:java.io.FilePermission','d: emphello', 'read');

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> connect kang/xxxxxx
연결되었습니다.
SQL> drop package my_java_utils;

패키지가 삭제되었습니다.

SQL> drop function get_salary;

함수가 삭제되었습니다.

SQL> drop procedure add_person;

프로시저가 삭제되었습니다.

D: emp>dropjava -u kang/xxxxxx TestDML FileTest



This article comes from dbakorea.pe.kr (Leave this line as is)

관련자료

댓글 0
등록된 댓글이 없습니다.

공지사항


뉴스광장


  • 현재 회원수 :  60,039 명
  • 현재 강좌수 :  35,848 개
  • 현재 접속자 :  88 명