달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

출처 : http://gurucode.egloos.com/1452887


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:\oracle\ora92\bin\loadjava.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:\temp>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:\temp>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:\temp>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:\temp>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:\temp>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:\temp>type hello

안녕!

나는 강명규라고 해!



SQL> conn system/xxxxxx

연결되었습니다.

SQL> exec dbms_java.grant_permission( 'KANG', 'SYS:java.io.FilePermission','d:\temp\hello', '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:\temp\hello') from dual;


MY_JAVA_UTILS.READ_FILE('D:\TEMP\HELLO')

--------------------------------------------------------------------------------

안녕!나는 강명규라고 해!null


SQL>


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



[작업한 내용 제거]

SQL> conn system/xxxxxx

SQL> exec dbms_java.revoke_permission( 'KANG', 'SYS:java.io.FilePermission','d:\temp\hello', 'read');


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


SQL> connect kang/xxxxxx

연결되었습니다.

SQL> drop package my_java_utils;


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


SQL> drop function get_salary;


함수가 삭제되었습니다.


SQL> drop procedure add_person;


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


D:\temp>dropjava -u kang/xxxxxx TestDML FileTest




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


:
Posted by 라면스프