zoukankan      html  css  js  c++  java
  • oracle 11g PL/SQL Programming学习十四

    ----------------------------------------------------------------------------
    -----------------PL/SQL学习笔记系列 By Cryking-----------------
    ------------------------转载请注明出处,谢谢!------------------------ 

    4.使用C共享库
    oracle为外部存储构建了一个可扩展的架构.
    它非常灵活地支持任何能被C调用的编程语言.

      定义C共享库(需要熟悉简单的C语言)
      你将使用下面的C程序作为一个动态链接库(DLL)或共享库.
    你需要安装一个C编译器来编译这个例子.
     writestr1.c文件内容

    /* Include standard IO. */
    #include <stdio.h>
    /* Declare a writestr function. */
    void writestr(char *path, char *message){
    /* Declare a FILE variable. */
    FILE *file_name;
    /* Open the file in write-only mode. */
    file_name = fopen(path,"w");
    /* Write to file the message received. */
    fprintf(file_name,"%s\n",message);
    /* Close the file. */
    fclose(file_name);
    }
    --编译一个C共享库
    [oracle@oracleserver oracle]$ vi writestr1.c
    [oracle@oracleserver oracle]$ gcc -shared -o writestr1.so writestr1.c
    [oracle@oracleserver oracle]$ ls *.so
    writestr1.so
    [oracle@oracleserver oracle]$ mkdir $ORACLE_HOME/customlib
    [oracle@oracleserver oracle]$ mv writestr1.so /u01/app/oracle/product/11.2.0/dbhome_1/customlib/


     
    你应当注意上面的C程序没有main函数,它只是作为一个DLL或共享库使用.
    注意上面的编译方法(不要直接使用 gcc -o编译,否则会报缺失main函数).

    你需要在$ORACLE_HOME目录下创建一个自定义目录customlib.
    你需要保证用户或组对customlib目录有读、写、执行的权限.

    定义和调用PL/SQL Library Wrapper
    首先,定义PL/SQL库.
      需要先在数据库定义外部库.(上面已经定义好,见listener.ora文件中的ENV参数.)
    创建PL/SQL库语法原型为:
    CREATE [OR REPLACE] LIBRARY <library_name> AS | IS
    '<file_specification>'
    AGENT '<agent_dblink>';
    /

    示例:

    23:46:18 SYS@ORCL> CREATE OR REPLACE LIBRARY library_write_string AS
    23:47:50   2  '/u01/app/oracle/product/11.2.0/dbhome_1/customlib/writestr1.so';
    23:47:52   3  /
    
    Library created.
    
    Elapsed: 00:00:00.23
    

    然后,建立PL/SQL library wrapper.
    PL/SQL library wrapper主要用来定义一个数据库和外部存储的接口.
    接口定义了PL/SQL参数和C数据类型的映射关系.
    ORACLE提供额外的派生类型来支持OCI.

    在PL/SQL和C数据类型转换之间存在以下限制:
      1.PL/SQL有NULL变量,但在C中没有对应的值.
        当一个变量可能为NULL时,你需要使用另外一个变量(指示器变量)来通知该变量是否为NULL。
        你使用OCI_IND_NULL和OCI_IND_NOTNULL来检查指示器变量是否为NULL.
      2.当数据交换时,C和PL/SQL都需要知道string字符串的长度.
        在处理二进制字符串时,你应当使用VSIZE来获得长度.
      3.当extproc代理运行在一个不同的数据库时,CHARSETID和CHARSETFORM受制于全球化的复杂性.
    创建一个C Library Wrapper存储过程的语法:
    CREATE [OR REPLACE] PROCEDURE name [parameter_list]
    AS EXTERNAL
    LIBRARY_NAME library_name
    NAME "<external_library_name>"
    AGENT IN [parameter_list]
    WITH CONTEXT
    PARAMETER [parameter_list];

    下面的示例提出了一个比较独特的方法,使用隐式的SELF.
    在PL/SQL,你不用去管理对象类型的SELF成员函数,因为它是隐式管理的.
    如:

    00:19:33 SYS@ORCL> CREATE OR REPLACE TYPE object_library_sample AS OBJECT(V NUMB
    ER,member function get_tea_temperature return number);
    00:21:06   2  /
    
    Type created.
    
    Elapsed: 00:00:00.45
    --使用了上面创建好的library_write_string PL/SQL库
    00:27:41 SYS@ORCL> CREATE OR REPLACE TYPE BODY object_library_sample AS
    00:27:43   2    MEMBER FUNCTION get_tea_temperature RETURN NUMBER AS
    00:27:43   3      LANGUAGE C NAME "tea_temp"
    00:27:43   4      library library_write_string
    00:27:43   5      WITH CONTEXT
    00:27:43   6      PARAMETERS(CONTEXT,
    00:27:43   7                 SELF   ,
    00:27:43   8                 SELF    INDICATOR STRUCT,
    00:27:43   9                 SELF    TDO,
    00:27:43  10                 RETURN  INDICATOR);
    00:27:43  11  END;
    00:27:44  12  /
    
    Type body created.
    
    Elapsed: 00:00:00.19
    


    创建PL/SQL外部存储.

    00:27:45 SYS@ORCL> CREATE OR REPLACE PROCEDURE write_string(path VARCHAR2, message VARCHAR2) AS
    01:00:25   2    EXTERNAL
    01:00:25   3    LIBRARY library_write_string NAME "writestr"
    01:00:25   4    PARAMETERS(path STRING, message STRING);
    01:00:26   5  /
    
    Procedure created.
    
    Elapsed: 00:00:00.22
    

    在你学会如何定义和配置PL/SQL wrapper前,你应当知道如何建立网络连接(配置监听)、编译共享库、代理配置等.
    完成这些后,现在你要知道如何使用它。

    --调用外部存储write_string
    01:00:28 SYS@ORCL> BEGIN
    01:05:19   2  write_string('/tmp/file.txt','Hello World!');
    01:05:19   3  END;
    01:05:21   4  /
    
    PL/SQL procedure successfully completed.
    
    Elapsed: 00:00:00.29
    


     

    根据之前建立的C共享库,可以知道执行该外部存储之后,会建立对应文件,写入传入的内容.
    验证:

    [oracle@oracleserver oracle]$ more /tmp/file.txt
    Hello World!

    使用外部存储时,有几点注意:
      1.你不应使用全局变量,因为它们不是线程安全的.
      2.你不应使用外部静态变量,因为它们不是线程安全的.
      3.你只能在支持DLL或共享库的平台上使用外部存储.
      4.你只能使用能被C调用的编程语言.
      5.当你需要传递游标或记录类型到外部存储的时候,你必须使用对象来传递
      6.你不能在PL/SQL wrapper的LIBRARY子句中使用DB_LINK.
      7.最多传递128个参数.其中浮点类型或双精度类型算2个参数.

     

    5.使用JAVA共享库
    oracle为外部存储构建了一个可扩展的架构.
    它非常灵活地支持任何能被C调用的编程语言.
    oracle直接支持java作为数据库的一部分.(除了oracle Express版)
    java库不使用extproc,因为它们已经是本地oracle数据库的一部分了.
    这简化了很多操作,但也带来了一些不便.(使用extproc代理和外部C库的时候)
    java相比C,有几点优势:
      1.JAVA理解SQL类型.它避免了乏味的数据类型映射.
      2.JAVA是加载到oracle数据库里的.
        这样避免了文件管理问题和监听ENV参数进程,因为它不需要使用extproc代理.
      3.JAVA是本地线程安全的.
        它不需要你去处理那些线程的细微差别,避免了静态变量.
      4.JAVA不需要去管理内存地址.(也就是说JAVA没有指针)
    java相比C,也有几点劣势:
      1.JAVA使用了SGA中的JAVA池来管理进程.而C外部存储使用它们自己的内存空间.
        换句话说就是C外部存储降低了SGA的内存消耗,而JAVA增加了SGA的负载.
      2.JAVA的速度比不上C.
        因为JAVA需要通过JVM来解释.
      3.JAVA限制对文件的访问.这是为了保护数据库的完整性.
        DBMS_JAVA包提供了一个方法来实现JAVA库程序的读写访问.
      4.使用java库的PL/SQL wrapper函数在方法定义上有限制.
        所有通过PL/SQL wrapper函数访问的JAVA class方法必须是静态的.
        这样支持PL/SQL wrapper函数的java库就不是线程安全的了.

    下面来实现定义一个简单的java库.
    首先,由于后面的程序包含了一个标准的I/O库,需要访问到外部物理文件.
    所以需要先使用DBMS_JAVA.GRANT_PERMISSION来对相应目录进行授权.

    01:51:44 SYS@ORCL>  BEGIN
    01:52:16   2     DBMS_JAVA.GRANT_PERMISSION('SCOTT',
    01:52:16   3                                'SYS:java.io.FilePermission',
    01:52:16   4                                '/home/oracle/file.txt',
    01:52:16   5                                'read');
    01:52:16   6   END;
    01:52:18   7  /
    
    PL/SQL procedure successfully completed.
    
    Elapsed: 00:00:28.79
    


     

    然后建立JAVA库.(你需要配置JAVA环境,也就是需要JDK+环境变量)
    提示:这里可以直接利用ORACLE_HOME/JDK/BIN路径下的JAVAC来编译java文件.
    编译ReadFile1.java,文件内容如下:

    import java.io.*;
    
    public class ReadFile1
    {
    // Convert the string to a file resource and call private method.
    public static String readString(String s){
      return readFileString(new File(s)); }
    // Read an external file.
      private static String readFileString(File file) {
    // Define control variables.
      int c;
      String s = new String();
      FileReader inFile;
      try {
      inFile = new FileReader(file);
      while ((c = inFile.read()) != -1) {
      s += (char) c; }
         }
    catch (IOException e) {
      return e.getMessage(); }
      return s; }
    // Testing method.
    public static void main(String[] args) {
      String file = new String("/tmp/file.txt");
      System.out.println(ReadFile1.readString(file)); }
    }
    


    将上面的java文件编译成ReadFile1.class后,使用loadjava工具将其加载到数据库中.

    [oracle@oracleserver ~]$ ls ReadFile1*
    ReadFile1.class
    [oracle@oracleserver ~]$ loadjava -r -f -o -user scott/tiger ReadFile1.class
    [oracle@oracleserver ~]$


    没有提示,说明ReadFile1.class文件加载成功.

    发布java库(也就是定义和使用PL/SQL Library Wrapper调用java库)
    注意JAVA库和PL/SQL也有数据类型的映射.LONG和LONG RAW数据类型最大只能为32K.
    大多数类型的映射是简单的.

    --建立函数,发布java类ReadFile1
    02:14:34 SCOTT@orcl> CREATE OR REPLACE FUNCTION read_string(file IN VARCHAR2)
    02:14:40   2  RETURN VARCHAR2 IS
    02:14:40   3    LANGUAGE JAVA NAME 'ReadFile1.readString(java.lang.String) return String';
    02:14:43   4  /
    
    Function created.
    
    Elapsed: 00:00:00.23
    


    使用函数read_string读取/home/oracle/file.txt文件的内容.

    --先在文件中写点内容
    [oracle@oracleserver ~]$ echo Just a test>>file.txt
    [oracle@oracleserver ~]$ more file.txt
    Just a test
    
    --然后使用函数读取
    02:21:20 SCOTT@orcl> SELECT read_string('/home/oracle/file.txt') a FROM dual;
    
    A
    ---------------
    Just a test
    
    1 row selected.
    
    Elapsed: 00:00:00.01
    



    6.外部存储故障诊断
    外部存储的失败通常是因为两种原因.
    一是监听的配置,共享库的配置、或者环境的配置.
      以下错误比较常见:
      1.监听的ENV参数不正确.
      2.文件路径问题
      3.监听中EXTPROC_DLLS参数值问题
      4.extproc监听不正确或没有运行
      5.没有单独的extproc监听
      6.PL/SQL Wrapper定义的NAME值不正确
     
    二是外部程序的定义和PL/SQL wrapper的定义不同.
      这一般是由于数据类型的不正确转换导致的.
    如错误:
    BEGIN
    *
    ERROR at line 1:
    ORA-28576: lost RPC connection to external procedure agent
    ORA-06512: at "PLSQL.WRITE_STRING", line 1
    ORA-06512: at line 4
    这种错误是由于数据类型不能隐式转换时发生.
    也就是当你尝试使用实际参数来fork外部库时,隐式转换到库的形式参数时发生的错误.

  • 相关阅读:
    解决前端从url路径上拿到的中文乱码
    仪表盘双层结构图
    element ui 按钮显示
    vue在手机上运行,打包之后显示
    echarts使用遇到的一些问题总结,比如颜色改变,文字位置调整
    微信小程序地图移动到指定的位置
    微信小程序多边形地图显示
    button取消边框
    代码审计小记 (一)
    [root-me](web-client)write up 一个大坑怎么填啊
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3137478.html
Copyright © 2011-2022 走看看