zoukankan      html  css  js  c++  java
  • JAVA SOURCE (1)

    用Java开发Oracle存储过程

    Java 2010-02-08 03:51:04 阅读375 评论0   字号: 订阅

    Java开发Oracle存储过程

    作者:Michael Klaene

    原文:http://www.developer.com/db/article.php/3337411

    译者:玄机逸士

     

    Oracle 8i开始,Oracle数据库就全面引入了实用的Java虚拟机 – Oracle JVMOracleJava之间的这种紧密关系,使得一个非常重要的技术,出现在Oracle开发人员面前:Java存储过程。通过Java存储过程,开发人员在创建数据库应用的时候,就可以充分利用Java提供的各种优势。本文将针对这个越来越受欢迎的技术进行讨论。其目标读者是,初懂OracleJava开发人员和略知JavaOracle PL/SQL开发人员。在强调Java存储过程的好处之后,我将示例说明在应用开发中,如何使用它们。

     

    Java存储过程的好处

    在很多情况下使用Java存储过程是很有意义的。鉴于Java当前受欢迎的程度,这种情况是完全可能的,即越来越多的开发人员对于Java的熟练程度要好于PL/SQLJava存储过程的出现,使得Java的开发人员可以用自己喜欢的语言来开发存储过程。对于有经验的PL/SQL开发人员,则可以利用Java语言的各种优点,来扩展数据库应用的功能。同时,Java使得编写独立于数据库的代码成为可能。更有意思的是,它允许你重用你已经存在的代码,从而大幅度地提高开发效率。

     

    正如你将看到的, PL/SQLJava 可以在同一个应用中和谐共处,因此,大可不必非此即彼地选择其中之一。 PL/SQL是一种针对Oracle数据库,经过高度优化的、优秀的过程语言,Java应用在Oracle数据库中运行也具有很好的扩展性。除此之外,通过OracleJVM来执行Java程序,可以充分利用高效的内存回收技术和线程管理方面的能力。

     

    Java存储过程,Step by Step

    简单地说,Java存储过程就是Java类,以schema对象的形式存储,通过调用规范(call specifications)OracleSQLPL/SQL可以对其访问。我们将看到,调用规范就是一些简单的PL/SQL声明,这些声明包装(wrap)’了存储在数据库中的Java方法。开发Java存储过程,有四个必须的步骤。下面我们逐一来看看这些步骤。

     

    #1. 编写Java

    第一步的妙处就是,它基本上和Oracle数据库没有什么关系。你只是简单地用你最喜欢的IDE,比如OracleJdeveloper,去开发一些Java类。如果想被用作存储过程,Java方法必须是publicstatic的。

     

    在移入Oracle数据库之前,你可以自由地编写、编译甚至对Java代码进行单元测试。事实上,对于通常的应用,这是一个很好的方式,因为这可以充分利用IDE的特性,诸如调试和代码生成。如果你想用OracleJVM来编译Java代码,后面将要说到的loadjava这个工具,将为你做这些工作。

     

    下面的代码列出了一个简单的Java - EmpManager。它包只含了一个简单的,用于向数据库插入一个emp(员工)记录的方法。

     

    import java.sql.*;

    import oracle.jdbc.*;

     

    public class EmpManager {

     

      //Add an employee to the database.

      public static void addEmp(int emp_id, String emp_f_name,

          String emp_l_name,float emp_salary, int dept_id) {

     

          System.out.println("Creating new employee...");

     

          try {

             Connection conn =

                DriverManager.getConnection("jdbc:default:connection:");

     

             String sql =

                "INSERT INTO emp " +

                "(emp_id,emp_f_name,emp_l_name,emp_salary,dept_id) " +

                "VALUES(?,?,?,?,?)";

             PreparedStatement pstmt = conn.prepareStatement(sql);

             pstmt.setInt(1,emp_id);

             pstmt.setString(2,emp_f_name);

             pstmt.setString(3,emp_l_name);

             pstmt.setFloat(4,emp_salary);

             pstmt.setInt(5,dept_id);

             pstmt.executeUpdate();

             pstmt.close();

             }

          catch(SQLException e) {

             System.err.println("ERROR! Adding Employee: "

               + e.getMessage());

             }

      }

    }

     

    到目前为止一切如常。在这个方法中,数据库链接URL"jdbc:default:connection:"。在写要在Oracle数据库中运行的Java代码时,可以利用一个特殊的服务器端的JDBC驱动程序。这个驱动程序使用用户缺省的链接,并提供对数据库的最快访问速度。

     

    #2. 加载Java

    我们的Java类将要编程一个真正的schema对象,所以必须将它移入数据库。为此,Oracle提供了一个命令行工具 – loadjava。这个loadjava工具提供了一个SQL CREATE JAVA等语句必要的接口,并且也可以用于将Java相关的文件加载到数据库中。

     

    由于现在还没有编译EmpManager.java,我们也可以让loadjava在加载的过程中顺便做完编译工作,这个可以通过打开 –resolve开关来做到。

     

    $ loadjava –u scott/tiger –v –resolve EmpManager.java

    –resolve 开关外, -v 将指示loadjava输出详细的(verbose)反馈, -u 用于指定数据库用户和密码。因为我们让loadjava编译源文件,因此源文件和类文件都将是SCOTT schema的成员。

     

    我们可以通过一个对USER_OBJECTS的查询来验证编译和加载的状态,如果正确,那么状态将是’VALID’的。

     

    SELECT object_name, object_type, status

    FROM user_objects WHERE object_type LIKE ‘JAVA%’;

     

    object_name object_type status

     

    EmpManager JAVA CLASS  VALID

    EmpManager JAVA SOURCE VALID

     

    反之,如果编译失败,可以在视图USER_ERRORS中看到具体的错误。

     

    如果选择用IDE编译,只需要简单将编译后的class文件加载即可,源文件可以保存在版本控制工具的文件系统中。Loadjava工具接受后缀为.sqlj (sqlj 源文件).properties.ser.jar.zip等文件。当后缀为.jar zip的文件时,Oracle将自动解压,并将各成员存储成单个的schema对象。

     

    在继续讨论之前,加载过程中有一个至关重要的组件值得一提:Oracle JVM resolver。典型地,一个JVMclasspath来定位Java类,以便被其他程序使用。在数据库中加载Java时,resolver来完成这样的任务。

     

    可以简单地认为resolver就是Oracle版本的classpathOracle将核心Java类存储与PUBLIC schema中。PUBLIC,就像你自己的schema一样,将被自动地引入缺省的resolver中。不过,如果需要引用另外一个schema中的类,那就必须提供你自己的’resolver规则(spec)’,这个可以通过加上-resolver开关来做到。比如,loadjava –u scott/tiger@test –resolve –resolver “((* SCOTT) (* PUBLIC) (* ADMIN))” ,指明了,在决定class依赖关系时,SCOTT schemaPUBLIC ADMIN3schema将被搜索。

     

    #3. 发布(publish)Java

    3步是发布Java类。Java类必须发布,以便可以直接从SQL或者PL/SQL直接访问。通过为其创建、编译一个调用规则来发布一个Java类。调用规则,通常被称为call spec或者PL/SQL包装器(wrapper),它将Java方法的参数、返回值类型映射成Oracle SQL的数据类型。下面给出的是addEmp方法的调用规则:

     

    CREATE OR REPLACE PROCEDURE add_emp (emp_id NUMBER,emp_f_name VARCHAR2,

      emp_l_name VARCHAR2, emp_salary NUMBER, dept_id NUMBER)

      AS LANGUAGE JAVA

      NAME 'EmpManager.addEmp(int, java.lang.String, java.lang.String,

                              float, int)';

    /

     

    add_emp过程为Java EmpManager.addEmp方法提供了一个SQL接口。Java方法,如果有相关包名,则必须使用包含包名的全名,并且,在开发一个调用对则时,Java对象如String也必须使用全名。

     

    作为一般性的规律,一个Java方法如果没有返回值,则被封装成过程,反之,则被封装成函数。现在,在EmpManager中,我们考虑加入第2Java方法,用于查询一个指定部门的员工人数:

     

    // 查询一个指定部门的员工人数

    public static int getEmpCountByDept(int dept_id) {

     

      Connection conn =

          DriverManager.getConnection("jdbc:default:connection:");

     

      String sql = "SELECT COUNT(1) FROM emp WHERE dept_id = ?";

      int cnt = 0;

     

      //Code here to add ResultSet value to cnt, trap SQLException, etc.

     

      return cnt;

    }

     

    它的调用规则指定其返回类型为NUMBER

     

    CREATE OR REPLACE FUNCTION get_emp_count_by_dept (dept_id NUMBER)

      RETURN NUMBER AS LANGUAGE JAVA

      NAME 'EmpManager.getEmpCountByDept(int) return int';

    /

     

    缺省地,就像标准的PL/SQL 过程一样,这些代码(译注:指上面的调用规则)只要有INVOKER权限就可以运行他们,换言之,当前用户有权执行它们。通过增加关键字AUTHID DEFINER,可以让其他用户以创建者的身份来执行它们。

     

    一旦执行,调用规则,则将其他文件作为SCOTT schema的成员加入数据库。

     

    #4. 调用过程

    我们已经开发、装载并发布了Java类。最后一步就是执行他们。缺省地,Java的输出被写入跟踪文件(trace files)DBMS_JAVA包,Oracle提供的用于管理服务器端Java的工具,可以将输出重定向到SQL*Plus

     

    SQL> SET SERVEROUTPUT ON

    SQL> CALL dbms_java.set_output(2000);

     

    现在,只要执行,Java的输出会显示在SQL *PLUS中,

     

    SQL> EXECUTE add_emp(1,'Joe', 'Smith',40000.00,1);

    Creating new employee...

     

    PL/SQL procedure successfully completed.

     

    正如你所看到的,从调用者的角度,调用Java存储过程和调用PL/SQL的存储过程或存储函数,并没有明显的区别。

     

    VARIABLE x NUMBER;

    CALL get_emp_count_by_dept(1) INTO :x;

    Getting Number of Employees for Dept...

     

    Call completed.

     

    PRINT x

     

    X

    ----------

    1

      

    SQLException类有getErrorCode() getErrorMessage()两个方法用于报错处理。Java存储过程中的任何未被捕获的异常将产生ORA-29532告警,Java调用也会被随之终止。至于如何处理异常,则不同的应用可能采用不同的方式。addEmp方法简单地捕获并显示异常。视图插入一个员工到一个无效的部门,将会收到一个错误消息。

     

    SQL> execute add_emp(2,'Tom', 'Jackson', 45000.00,2);

    Creating new employee...

    ERROR! Adding Employee : ORA-02291: integrity constraint

                             (OPS$AK4353.FK_DEPT_ID) violated -

                             parent key not found

     

    由于有从PL/SQL来调用Java的需要,自然就会想到也有从Java调用PL/SQL的需要,这个可以通过在Java方法中使用CallableStatement对象,而轻易做到。

     

    CallableStatement cstmt = conn.prepareCall("{my_plsql_proc}");

     

    因此,可以创建一个无缝的环境,使得PL/SQL轻易调用Java,反之亦然。

     

    一种使用方案

    理解Java存储过程越好,将对你的开发时间越有帮助。一个通常的方法就是,在需要考虑数据库访问效率时(译注:建立数据库连接很耗时间和资源),就会使用PL/SQL。现在Java就可以更加轻松地做到这点。开发各个类,遵照必要的调用规则即可。

     

    或许有这样的可能,比如一个数据库应用需要与操作系统文件和目录进行交互。Oracle UTL_FIL这个包提供访问操作系统文件的功能极为有限,而Java则有丰富得多的File IO能力,允许开发人员删除文件,增加目录等等。所以为什么不利用它呢? 再比如,命令行PL/SQL程序的使用者,也可能将job参数存放在一个配置文件中,你可以编写Java方法来读取这些参数:

     

      public static String readFile (String usrFile)  {

         String fileStr = new String();

         try {

            File file = new File(usrFile);

            FileReader fr = new FileReader(file);

            LineNumberReader lnr = new LineNumberReader(fr);

            ...

            ...

         }

         catch(Exception e) {

            ...

         }

         return fileStr;

      }

     

    然后,PL/SQL包会为你写的这个或者任何其他FILE IO方法定义调用规则。

     

    CREATE OR REPLACE PACKAGE my_java_utils IS

     

    FUNCTION read_file (file VARCHAR2) RETURN VARCHAR2;

     

    END my_java_utils;

    /

     

    CREATE OR REPLACE PACKAGE BODY my_java_utils IS

     

    FUNCTION read_file (file VARCHAR2) RETURN VARCHAR2

      AS LANGUAGE JAVA

      NAME 'MyJavaUtils.readFile(java.lang.String) return java.lang.String';

     

    END my_java_utils;

    /

     

    PL/SQL过程可以调用read_file这个Java存储过程, 而它则以一个文件的数据作为输入。同时使用两个世界最好的方法,开发人员可以轻易开发出鲁棒的数据库应用。必须注意到,在这个特定的方案中,需要有访问文件系统的一定权限。更进一步的信息,可以查询OracleJava安全方面的文档。

     

    总结

    Oracle提供了很多Java存储过程方面的文档(译注:可以到OTN上找)。如果需要,你可以下载本文示例代,以便做更加细致的试验。

     

    在这篇文章中,给出了有关Java存储过程的总的看法,同时示例说明了如何实现它们。Java存储过程是一种很强大的技术,值得一个Oracle开发人员在构建数据库应用时,考虑使用之。

  • 相关阅读:
    poj1877
    poj2163
    API hook 单步调试
    用VC++编写勾子程序(转)
    不使用DLL创建全局系统钩子
    接触DLL编写并实现线程注入和全局钩子
    根据RGB计算亮度
    !!!光线对视频识别技术的影响
    消息钩子函数入门篇
    “蓝脑”计划:人造大脑的可能性 文化·探索 CCTV_com
  • 原文地址:https://www.cnblogs.com/Mayvar/p/wanghonghua_201102300157.html
Copyright © 2011-2022 走看看