SQLJ运行时刻程序包可在JServer Java虚拟机( JVM)中找到。 还有,你不必编写创建到数据库的连接的代码了,因为JServer本身带有到数据库服务器的隐含的连接。由于你将在客户机上编译源代码,所以必须包括下列三个import语句:
import oracle.jdbc.driver.OracleConnection;import sqlj.runtime.ref.DefaultContext;import sqlj.runtime.ExecutionContext;接下来,需要声明迭代程序类。在迭代程序类声明中定义了SQLJ为你构造的Java类。你必须把SQLJ迭代程序声明和连接上下文说明放在你的SQLJ源文件里的Java类的顶端,紧接着import语句之后。 下列三个迭代程序声明提供PL/SQL版的三个游标函数:
#sql iterator StartEndDate(Timestamp startDate, Timestamp endDate);#sql iterator ValidTransactions(Long transactionId);#sql iterator ClassificationCount(long);使用下面三个参数定义PipelineReset.java类的静态DeleteClassification方法:
public class PipelineReset {public static void DeleteClassification(String periodName, int commitBatchSize,int percentageDeleted) throws SQLException {...}}现在我们把代码放入方法中。 首先你需要实例化一个迭代程序对象决定具体时期的起始和结束时间。 使用periodname参数:
StartEndDate dateIter;Timestamp endDate = null;Timestamp startDate = null;#sql dateIter = { select startDate, endDatefrom Periodwhere description = :periodName };if (dateIter.next()) {startDate = dateIter.startDate();endDate = dateIter.endDate();};dateIter.close();StartEndDate dateIter;下一步是实例化ValidTransactions类型的迭代程序对象并把你的具体时期内全部的TransactionId都放入ArrayList类型的vtIdList对象中。 在PL/SQL版本中,这个工作是由游标csrTransactToReset完成的。
ValidTransactions vtIter;#sql vtIter = { select T.TransactionIdfrom ValidTransaction Twhere T.TransactionDate >= :startDateand T.TransactionDate <= :endDateand exists (select '1'from Classification Cwhere C.TransactionId = T.TransactionId) };ArrayList vtIdList = new ArrayList();while (vtIter.next()) {vtIdList.add((Long) vtIter.TransactionId());}vtIter.close();现在在vtIDList对象中循环,删除Classification中TransactionId每个值所对应的行。
ExecutionContext ec = DefaultContext.getDefaultContext().getExecutionContext();ClassificationCount countIter;long vDeletedRows = 0;long vTotalRows = 0;int vCount = 0;if (vtIdList.size() > 0) {#sql countIter = { select count(*) from Classification };#sql { fetch : countIter into :vTotalRows };countIter.close();Iterator itr = vtIdList.iterator();while (itr.hasNext()) {vCount ;#sql [ec] { delete from Classificationwhere TransactionId = :((Long) itr.next()) };vDeletedRows = vDeletedRows ec.getUpdateCount();if ((vCount % commitBatchSize) == 0) {#sql [ec] { commit };}}#sql [ec] { commit };}最后,你必须决定你是否要重建索引。 从你的SQLJ代码中调用相同的RebuildIndex PL/SQL存储过程:
if ((((float) vDeletedRows / (float) vTotalRows) * 100) >=percentageDeleted ) {#sql [ec] {call RebuildIndex('CLASSIFICATION_PK_IND','parallel nologging compute statistics')};} 代码清单2 显示了PipelineReset.sqlj文件的完整的清单。
你可以在OracleHome/sqlj/demo目录中找到一个样本sqlj.properties文件。下面给出了用来翻译和编译SQLJ源文件的命令:
sqlj -props=translator.properties -explain -ser2class -status PipelineReset.sqlj推荐在SQLJ命令行中包括- explain和- status选项,这样就能显示并很容易了解翻译和编译步骤。 使用- ser2class标志把生成的配置文件(.ser文件)转换成.class文件。 在本例中,用六个类文件结束:主要的PipelineReset.class,三个迭代程序类( StartEndDate.class,ValidTransactions.class和ClassificationCount.class)以及配置类( PipelineReset_SJProfile.class和PipelineReset_SJProfileKeys.class)。使用下列命令把它们封装到一个PipelineReset.jar Java压缩文件中:
jar cvf0 PipelineReset.jar *.class现在需要把生成的类载入数据库中。 第三步:把Java文件装载入数据库服务器 Oracle提供了一个默认的使用Oracle JDBC OCI8驱动程序的loadjava实用程序来完成连接。下面就是用一个ORCL数据库和一个用户scott/tiger做为参数来使用这个实用程序的语法:
loadjava -user scott/tiger@ORCL -resolve PipelineReset.jar还可以指定Oracle JDBC瘦驱动程序:
loadjava -thin -user scott/tiger@server:1521:ORCL -resolve PipelineReset.jar使用- resolve标志直接地分析类的引用而不是等待每个类被第一次调用。 在User_Objects Oracle词典中运行查询,使loadjava把所有的类都成功加载。
select object_name,statusfrom User_Objectswhere object_type = 'JAVA CLASS'order by object_name;下面是上面查询的输出结果:
OBJECT_NAME STATUS------------------------------ -------ClassificationCount VALIDPipelineReset VALIDPipelineReset_SJProfile0 VALIDPipelineReset_SJProfileKeys VALIDStartEndDate VALIDValidTransactions VALID6 rows selected.正如所见,你所有的Java类都被成功地载入数据库中了。 第四步:为Java存储过程创建PL/SQL封装程序 即使Java代码已经在数据库中了,但是它还是不能真正的可用。 Oracle数据库不能直接调用Java类方法。 它通过PL/SQL间接调用它们并把它们传到JServer Java虚拟机中来运行。这就是为什么需要为PipelineReset类的DeleteClassification静态方法创建一个PL/SQL封装程序(也就是在数据库服务器中发布Java方法)。 下面的脚本将创建PipelineResetFull程序包的一个JDeleteClassification过程。 这个过程是PipelineReset Java类的DeleteClassification静态方法的PL/SQL封装程序。
create or replace package PipelineResetFull asprocedure JDeleteClassification(periodName in varchar2,commitBatchSize in number,percentDeleted in number);end PipelineResetFull;/create or replace package body PipelineResetFull asprocedure JDeleteClassification(periodName in varchar2,commitBatchSize in number,percentDeleted in number)aslanguage javaname 'PipelineReset.DeleteClassification(java.lang.String,int,int)';end PipelineResetFull;/例如:下列的语法是错误的:
...name 'PipelineReset.DeleteClassification(String,int,int)';...它能够编译,但是将抛出运行时间异常:
ERROR at line 1:ORA-29531: no method DeleteClassification in class PipelineReset这是怎么发生的呢?DeleteClassification不就在PipelineReset类中吗? 事实上,它是这个类中的唯一的方法! Oracle不能匹配PL/SQL封装程序中的DeleteClassification方法与一个实际类的方法。 在这种情况下,你必须明确地指定一个完全符合规格的类的路径,除非它是一个基类,就像在我们例子中的int Java类型。 第五步:测试Java存储过程 在数据库中发布Java代码之后,我们就会发现在PL/SQL和Java过程之间就没有用法差异了。可以使用下列代码从SQL * Plus中调用并测试PL/SQL和Java代码:
beginDeleteClassification('January 1999', 10000, 10);End;/beginPipelineResetFull.JDeleteClassification('January 1999', 10000, 10);end;/ 现在我们甚至不能分辩出哪个是Java过程,因为它已经完全和PL/SQL环境整合起来了。
代码段2:
import java.sql.SQLException;import java.sql.Timestamp;import java.util.ArrayList;import java.util.Iterator;import oracle.jdbc.driver.OracleConnection;import sqlj.runtime.ref.DefaultContext;import sqlj.runtime.ExecutionContext;#sql iterator StartEndDate (Timestamp startDate, Timestamp endDate) ;#sql iterator ValidTransactions (Long transactionId) ;#sql iterator ClassificationCount (long) ;public class PipelineReset{public static void DeleteClassification(String periodName, int commitBatchSize,int percentageDeleted) throws SQLException {StartEndDate dateIter;ValidTransactions vtIter;ClassificationCount countIter;Timestamp endDate = null;Timestamp startDate = null;int vCount = 0;long vDeletedRows = 0;long vTotalRows = 0;#sql dateIter = { select startDate,endDatefrom Periodwhere description = :periodName };long startTime = System.currentTimeMillis(); if (dateIter.next()) {startDate = dateIter.startDate();endDate = dateIter.endDate();};dateIter.close();#sql vtIter = { select T.TransactionIdfrom ValidTransaction Twhere T.TransactionDate >= :startDateand T.TransactionDate <= :endDateand exists (select '1'from Classification Cwhere C.TransactionId = T.TransactionId) };fetched from vtIter iterator */ArrayList vtIdList = new ArrayList();while (vtIter.next()) {vtIdList.add((Long) vtIter.TransactionId());}vtIter.close();ExecutionContext ec = DefaultContext.getDefaultContext().getExecutionContext();if (vtIdList.size() > 0) {#sql countIter = { select count(*) from Classification };#sql { fetch : countIter into :vTotalRows };countIter.close();Iterator itr = vtIdList.iterator();while (itr.hasNext()) {vCount ;#sql [ec] { delete from Classificationwhere TransactionId = :((Long) itr.next()) };vDeletedRows = vDeletedRows ec.getUpdateCount();if ((vCount % commitBatchSize) == 0) {#sql [ec] { commit };}}#sql [ec] { commit };} if ((((float) vDeletedRows / (float) vTotalRows) * 100) >=percentageDeleted ) {#sql [ec] { call RebuildIndex('CLASSIFICATION_PK_IND','parallel nologging compute statistics') };} } long endTime = System.currentTimeMillis();System.out.println("Total time : " ((endTime - startTime) / 1000) " sec.");}}