使用SQLJ进行数据库开发 第二部分:SQLJ语言元素

在我的第一篇文章中,我描述了什么是SQLJ,把它同PL/SQL和JDBC进行了比较,并分析了从SQLJ获得的好处。在这篇文章中,我阐述了SQLJ编程语言的基本原理,这样你能做好准备,以使用真正的SQLJ。
   
    SQLJ程序是嵌入了SQL语句的标准Java程序,这些SQL语句从#sql标记开始,到分号结束。有两种SQLJ语句:声明和可执行语句。
   
    声明语句宣布连接环境和迭代器。连接环境被用于建立数据库连接,而迭代器被用于保存SQL查询的结果集。这二者中,后者被用得更多。可执行语句执行嵌入的SQL语句和PL/SQL块。因为SQLJ程序要被翻译和通过JDBC运行,所以任何被JDBC驱动程序支持的SQLJ语句(参加下面内容)都可以被嵌入进SQLJ可执行语句。可执行语句也可以包含主机表达式,通过Java变量,它被用来在Java程序和数据库之间交换信息。
   
    Oracle JDBC驱动程序
    Oracle提供下面的JDBC驱动程序:
    客户端廋驱动程序是一个100%的Java驱动程序,由没有安装Oracle的客户端使用,Oracle推荐通过applets使用它。当Java applet运行时,浏览器能够下载这些驱动程序。
    OCI驱动程序(OCI8和OCI7)通过Oracle客户安装,客户端使用这些驱动程序。Oracle JDBC OCI驱动程序通过从Java直接调用Oracle调用接口(OCI)访问数据库,提供Oracle 7、8和8i版本之间最高的兼容性。这些驱动程序需要通过Net8的Oracle客户安装。
    服务器端廋驱动程序提供同客户端廋驱动程序相同的功能,但是它运行在Oracle数据库内部,并且可以访问远程数据库。作为一个中间件,从一个Oracle服务器访问一个远程的Oracle服务器,或者更一般地,从内部其它的Oracle服务器,如从任何Java存储过程或EJB访问一个Oracle服务器时,这种驱动程序很有用。
    服务器端内部驱动程序,叫做KPRB(核心程序包),支持运行在完成SQL操作的目标Oracle数据库内部的任何Java代码。服务器端内部驱动程序允许JServer JVM直接同SQL引擎通讯,在Oracle 8i/9i服务器上,它是作为存储过程、存储函数、中间件、EJB或者CORBA对象运行的Java代码的默认JDBC驱动程序。
   
    由于运行在Oracle JServer内部,同时被专门优化,KPRB JDBC驱动程序非常灵活和有效率。你在写SQLJ存储过程时,将会用到它。
   
    接下来,让我们分析描述的SQLJ元素:连接环境、迭代器、可执行语句和主机表达式。 
   
    连接环境
    要建立一个简单的连接,在构造DefaultContext对象的时候,可以使用一个DefaultContext的实例,指定数据库的URL、用户名和密码来实现,而最容易的方法是使用由Oracle公司提供的oracle.sqlj运行时的connect()方法。在下面的例子中,我们将使用JDBC廋驱动程序,通过1521端口把用户“scott”连接到服务器MYSERVER的数据库,密码是“tiger”,ORCL是被连接的数据库的SID:
    Oracle.connect("jdbc:oracle:thin@MYSERVER:1521:ORCL", "scott", "tiger");
    这个例子创建DefaultContext类的一个实例,并且把它安装为你默认的连接。有了这个DefaultContext的实例,不再需要其他的任何东西。
   
    要建立多个连接,可以通过Oracle.getConnection()方法,创建和使用DefaultContext类另外的实例。在在下面的例子中,我们将使用Oracle OCI8驱动程序,把MYSERVER_ORCL作为一个Oracle服务器的名字,在TNSNames.ora中创建一个ORCL实例:
    DefaultContext myContext1 = Oracle.getConnection
    ("jdbc:oracle:oci8@MYSERVER_ORCL", "scott", "tiger");
    DefaultContext myContext2 = Oracle.getConnection
    ("jdbc:oracle:oci8@MYSERVER_ORCL ", "tom", "bear");

   
    这段代码创建两个连接环境实例,他们都使用相同的Oracle OCI8驱动程序,但是使用不同的schemas。通过为每一个语句指定连接,在这两个schemas中都可以完成SQL操作:
    #sql [myContext1] { SQL statement };
    ...
    #sql [myContext2] { SQL statement };

   
    在程序的最后,我们必须提交或者撤销所有的修改,并且在FINALLY子句和TRY/CATCH块中关闭连接:
     finally
    {
       #sql [myContext1] { commit };
       myContext1.close();
   
       #sql [myContext2] { commit };
       myContext2.close();
    }
    ...

   
    迭代器(iterator)
    iterator对象用于保存数据,在SQLJ程序中,SQL查询返回的结果集也可以用iterator对象表示。iterator对象是iterator类的实例,概念类似于PL/SQL的游标。
   
    要使用iterator处理SQL查询返回的行(rows,可以理解为记录),必须完成以下五步:
    1.声明iterator类。
    2.声明一个来自iterator类的iterator对象。
    3.使用一个SELECT语句填充iterator对象。
    4.从iterator对象读出各行。
    5.关闭iterator对象。
   
    有两种iterator类:
    1.命名(Named)iterators,必须指定Java变量类型和iterator列名(column name)。
    2.定位(Positional)iterators,只有被检索的数据库列的Java变量必须被指定。 
   
    命名Iterators:一个命名iterator声明同时指定列的访问名称和他们的Java变量。 
   
    下面让我们用例子示范所有五个步骤,假设想检索Emp表的Ename、Job和HireDate列,用于查看薪水超过1500的职员。
   
    1.声明iterator类:
    #sql iterator EmpIteratorClass(String Ename, String Job, Timestamp HireDate);
    Java String类被用于表示Ename和Job列,因为它与Oracle VARCHAR2数据库的数据类型兼容。java.sql.Timestamp类型被用于表示HireDate列(Oracle的DATE数据类型),因为java.sql.Date类型只能保存年、月、日和时间信息,不能像java.sql.Timestamp一样可以保存时、分和秒。
   
    2.声明一个来自iterator类的iterator对象:
    EmpIteratorClass empIterator;
   
    3.使用一个SELECT语句填充iterator对象。下面的SQLJ语句用Emp表的Ename、Job和HireDate列填充empIterator对象:
    int salary = 1500;
    #sql empIterator = {
       select Ename, Job, HireDate
          from Emp
          where Sal > :salary
    };

    我们也声明主机变量salary,它被用于WHERE子句,识别从Emp表检索的行。记住,SQL查询返回的数据库列的名字必须与在第一步定义的iterator列名相符。
   
    4.从iterator对象读出各行。因为iterator对象可以包含多个行,我们必须使用循环依次访问每一行――我们从PL/SQL游标读出每一行时,使用了相同的方法。命名iterator 实现了一个next()方法,调用它可以从头到尾移动iterator对象的行。另外,SQLJ提供了检索iterator值的方法。 
    下面的代码循环打印姓名、工作和雇佣的日期:
    while (empIterator.next()) {
       System.out.println("Name: " + empIterator.Ename());
       System.out.println("Job: " + empIterator.Job());
       System.out.println("Hire Date:" + empIterator.HireDate().toString());
    }

   
    5.关闭iterator对象:
    empIterator.close();
   
    清单1结合了第二到第五步,通过命名iterator和empSalary参数展示listEmployees()方法。
    清单1. Iterator类声明在listEmployees()方法的外部
    public static void listEmployees(String empSalary)
    throws SQLException {
       EmpIteratorClass empIterator;
       Integer salary = new Integer(empSalary);
       try {
          #sql empIterator = {
             select Ename, Job, HireDate
                from Emp
                where Sal > :salary
          };
          while (empIterator.next()) {
             System.out.println("Name: "  + empIterator.Ename());
             System.out.println("Job: "  + empIterator.Job());
             System.out.println("Hire Date:" + empIterator.HireDate().toString());
          }
          empIterator.close();
       } catch (SQLException e) {
         System.err.println("SQLException" + e);
         System.exit(1);
       }
    }

   
    定位Iterators:同命名iterators相比,定位iterators仅仅指定列的编号(number)和类型,而不指出他们的名字。列数据可以仅仅通过他们的位置来访问,同传统的(指PL/SQL)FETCH…INTO语法完全相同。另外,使用FETCH语句,你也必须使用定位iterator的方法endFetch()来探测终止的条件以跳出循环。在取得的数据被访问前,总是必须检查这个条件。
   
    下面是使用定位iterator的例子时相同的五个步骤:
   
    1.声明iterator类:
    #sql iterator EmpIteratorClass(String, String, Timestamp);
   
    2.声明一个来自iterator类的iterator对象,同时声明所有必须的主机变量,用于从iterator对象取得数据:
    EmpIteratorClass empIterator;
    String name = null;
    String job = null;
    Timestamp hireDate = null;

   
    3.使用一个SELECT语句填充iterator对象:
    int salary = 1500;
    #sql empIterator = {
       select Ename, Job, HireDate
          from Emp
          where Sal > :salary
    };

   
    4.从iterator对象读出各行,保存到主机变量:
    while (true) {
       #sql { FETCH :empIterator INTO :name, :job, :hireDate };
       if (empIterator.endFetch()) {
          break;
       }
   
       System.out.println("Name: " + name);
       System.out.println("Job: " + job);
       System.out.println("Hire Date:" + hireDate().toString());
    }

   
    5.关闭iterator对象:
    empIterator.close();
   
    清单2结合了第二到第五步,通过定位iterator和empSalary参数展示listEmployees()方法。
    清单2. 使用定位Iterator和empSalary参数的listEmployees()方法:
    public static void listEmployees(String empSalary)
    throws SQLException {
       EmpIteratorClass empIterator;
       Integer salary = new Integer(empSalary);
       /* Host variables */
       String name = null;
       String job = null;
       Timestamp hireDate = null; 
       try {
          #sql empIterator = {
             select Ename, Job, HireDate
                from Emp
                where Sal > :salary
          };
          while (true) {
             #sql { FETCH :empIterator INTO :name, :job, :hireDate };
             if (empIterator.endFetch()) {
                break;
             }
   
             System.out.println("Name: "     + name);
             System.out.println("Job: "      + job);
             System.out.println("Hire Date:" + hireDate().toString());
          }
          empIterator.close();
       } catch (SQLException e) {
         System.err.println("SQLException" + e);
         System.exit(1);
       }
    }
   
    如你所见,我们使用与PL/SQL游标语法非常相似的语法操纵了全部的定位iterator对象。命名iterators和定位iterators都完成相同的基本功能:保存SQL查询返回的多行结果集。使用哪种类型的iterator是一件麻烦事,或者说出于个人的偏爱。从性能的观点看,他们产生同样的结果。
   
    可执行语句
    可执行的SQLJ语句包含一个位于大括号之内的静态SQL操作。有两种可能的可执行语句类型,这取决于SQL是否返回值。 
   
    这是一个没有返回值的嵌入SQL语句的例子,它在Emp表中创建一个关于Ename和Sal列的复合索引:
    #sql { create index EMP_ENAME_SAL on Emp(Ename, Sal) };
    如果嵌入SQL语句返回值,必须使用主机变量,以指定结果放在哪里。在这个例子中,调用PL/SQL 的getSalary函数返回Empno=7900的雇员的薪水。你可以使用VALUES或SET操作符调用函数,即:
    int salary;
    int empNo = 7900;
    #sql salary = { VALUES getSalary(:empNo) };
    或者;
    #sql { SET :salary = getSalary(:empNo) };

   
    主机表达式
    主机变量,如你在前面的例子中所见,允许SQLJ程序在数据库和Java程序之间交换数据。他们是在Java程序中声明并且在SQLJ语句中引用的任何Java变量。主机变量使用冒号前缀嵌入进SQLJ语句,并被主机表达式调用。他们绑定主机变量到SQLJ可执行语句,并且也可以包含Java数组元素、对象属性或者Java函数,SQLJ将处理在SQL和Java环境之间来回地移动的数据。
   
    在SQLJ中,所有标准的JDBC类型――如Boolean、byte、short、int、String、byte〔〕、double、float、java.sql.Data等等――都是有效的主机表达式类型。另外,Oracle的SQLJ翻译器支持使用Oracle类型,如ROWID、CLOB、BLOB,以及Object和REF类型。
   
    在这篇文章中,我讲述了所有主要的SQLJ类型的对象,要开始写实际的SQLJ代码,这些都是必不可少的:连接环境、命名和定位迭代器(iterators)、可执行语句和主机表达式。
   
    Boris Milrud 有十年的软件开发经验,他是位于San Jose CA(圣何塞市)Callidus软件公司的高级数据库工程师,他专长于各种Oracle数据库软件开发,包括数据库设计、编程、优化和调试。可以通过milrud@hotmail.com和他联系。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值