【摘要】
集算器提供了 JDBC 驱动,很容易嵌入到 Java 程序调用,方法和在 Java 中执行 SQL 和存储过程类似。
结构图如下:

1. 加载驱动 jar
集算器 JDBC 类似一个不带物理表的数据库 JDBC 驱动,可以把它简单的看成是一个只有存储过程的数据库。另外,集算器 JDBC 是个完全嵌入式计算引擎,已经在 JDBC 中完成了所有运算,不象数据库那样 JDBC 只是个接口,实际运算在独立的数据库服务器完成。
如果在 web 应用项目下,可以把这些 jar 包放在 WEB-INF/lib 目录下。集算器 JDBC 需要三个基础 jar 包,都可以在 [安装目录]\esProc\lib 目录下找到:
dm.jar //集算器计算引擎及JDBC驱动包
icu4j_3_4_5.jar //处理国际化
jdom.jar //解析配置文件dm.jar //集算器计算引擎及JDBC驱动包icu4j_3_4_5.jar //处理国际化 jdom.jar //解析配置文件
除了以上的必需 jar,还有一些为完成特定功能的 jar 包:
比如数据库作为数据源,那么还需要相应数据库的驱动 jar 包;
要读写 Office 文件,则需要加入 poi*.jar 和 xmlbeans.jar;
要使用绘制图形功能,则需要加入 SVG 图形处理相关的 jar 包,包括 batik*.jar、js.jar、pdf-transcoder.jar、xalan-2.6.0.jar、xercesImpl.jar、xml-apis.jar、xml-apis-ext.jar。
2. 部署 raqsoftConfig.xml
集算器还有个重要的配置文件 raqsoftConfig.xml,可以在 [安装目录]\esProc\config 下找到,需复制后放置在应用项目的类路径下,配置文件的名称不可改变。
在 raqsoftConfig.xml 文件中,配置了授权信息、集算器主路径、dfx 文件寻址路径等各类信息。我们先看下最基本的配置,即集算器授权文件配置:
<?xml version="1.0" encoding=" UTF-8"?>
< Config Version="2">
<Runtime>
<Esproc>
<!--集算器授权文件配置,可以是绝对路径,也可以是相对路径,使用相对路径时是相对于类路径-->
<license>esproc.xml</license>
<!--试用授权文件可从润乾公司官网中下载-->
</Esproc>
</Runtime>
</Config><?xml version="1.0" encoding=" UTF-8"?>< Config Version="2">
<Runtime>
<Esproc>
<!--集算器授权文件配置,可以是绝对路径,也可以是相对路径,使用相对路径时是相对于类路径-->
<license>esproc.xml</license>
<!--试用授权文件可从润乾公司官网中下载-->
</Esproc>
</Runtime></Config>
Java 调用
Java 程序中调用 SPL 脚本 ,与 java 中调用 SQL 和存储过程类似,下面来看下具体是怎样实现的:
执行 SPL 语句
比如创建一个数据表,并添加两个字段 baseNum、square2,分别将 100 以内的自然数及其的平方值组成 100 条记录插入到数据表中,最后将表中的数据作为结果集返回。
Java 代码如下:
public void runSPL() throws ClassNotFoundException, SQLException{
Connection con = null;
PreparedStatement st;
ResultSet set ;
//建立连接
Class._forName_("com.esproc.jdbc.InternalDriver");
con= DriverManager._getConnection_("jdbc:esproc:local://");
//直接执行SPL语句,返回结果集
st = (PreparedStatement)con.createStatement();
ResultSet rs = st.executeQuery("=100.new(~:baseNum,~*~:square2)");
//简单处理结果集,将结果集中的字段名与数据输出
ResultSetMetaData rsmd = rs.getMetaData();
int colCount = rsmd.getColumnCount();
for ( int c = 1; c <= colCount;c++) {
String title = rsmd.getColumnName(c);
if( c > 1 ) {
System._out_.print("\t");
}
else {
System._out_.print("\n");
}
System._out_.print(title);
}
while (rs.next()) {
for(int c = 1; c<= colCount; c++) {
if ( c > 1 ) {
System._out_.print("\t");
}
else {
System._out_.print("\n");
}
Object o = rs.getObject(c);
System._out_.print(o_.toString());
}
}
//关闭连接
if (con!=null) {
con.close();
} public void runSPL() throws ClassNotFoundException, SQLException{
Connection con = null;
PreparedStatement st;
ResultSet set ; //建立连接
Class._forName_("com.esproc.jdbc.InternalDriver");
con= DriverManager._getConnection_("jdbc:esproc:local://"); //直接执行SPL语句,返回结果集
st = (PreparedStatement)con.createStatement();
ResultSet rs = st.executeQuery("=100.new(~:baseNum,~*~:square2)"); //简单处理结果集,将结果集中的字段名与数据输出
ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); for ( int c = 1; c <= colCount;c++) {
String title = rsmd.getColumnName(c); if( c > 1 ) {
System._out_.print("\t");
} else {
System._out_.print("\n");
}
System._out_.print(title);
} while (rs.next()) { for(int c = 1; c<= colCount; c++) { if ( c > 1 ) {
System._out_.print("\t");
} else {
System._out_.print("\n");
}
Object o = rs.getObject(c);
System._out_.print(o_.toString());
}
} //关闭连接
if (con!=null) {
con.close();
}
执行结果:
在 SPL 中访问本地文件
通过 SPL 还可以访问本地的文件,其中包括 Txt、Excel、Json、Csv、Ctx 等多种类型的文件,访问时可以通过绝对路径查文件位置,也可以通过相对路径查找,使用相对路径时,则是相对于配置文件中的主目录,所以,首先我们来配置下主目录:
在 raqsoftConfig.xml 文件的节点 < Esproc ></ Esproc > 中添加以下节点:
<!--集算器主路径,该路径为单一的绝对路径-->
<mainPath>D:\mainFile</mainPath> <!--集算器主路径,该路径为单一的绝对路径-->
<mainPath>D:\mainFile</mainPath>
我们把要调用的文件 employee.txt 放到主目录下面,在 JAVA 中调用时,建立连接、输出结果等部分的代码与上例是完全相同的,调用 SPL 语句部分如下:
ResultSet rs=st.executeQuery("=file(\"D:\mainFile\employee.txt\").import@t()");ResultSet rs=st.executeQuery("=file(\"D:\mainFile\employee.txt\").import@t()");
这里支持绝对路径与相对路径的使用,在 Java 中使用反斜杠 \ 表示转义符。
对于这种简单运算,还可以使用简单 SQL 语法:
ResultSet rs=st.executeQuery("$()select * from employee.txt");ResultSet rs=st.executeQuery("$()select * from employee.txt");
其中 $() 表示访问本地文件系统,两种写法的结果集相同。
带参数的 SPL 语句
参数是 SQL 语句的一个重要组成部分,同样,SPL 语句中也支持参数的使用,例如,像上例中,要查询 employee.txt 文件中的数据,但是要求只查询工资在 12000 到 20000 之间的记录,并根据工资升序排序:
调用部分代码如下:
PreparedStatement pst = con.prepareStatement("$()select * from employee.txt where SALARY > ? and SALARY< ? order by SALARY");
//设置参数
pst.setObject(1,12000);
pst.setObject(2,20000);
ResultSet rs = pst.executeQuery(); PreparedStatement pst = con.prepareStatement("$()select * from employee.txt where SALARY > ? and SALARY< ? order by SALARY"); //设置参数
pst.setObject(1,12000);
pst.setObject(2,20000);
ResultSet rs = pst.executeQuery();
其中用?表示参数,然后通过 setObject() 为上面的参数一一赋值。
有数据源的 SPL 语句
集算器 JDBC 既然是个数据计算引擎,那么数据来源的重要途径之一就是数据库了,JAVA 中如何来调用带数据源的 SPL 语句呢?往下看:
JAVA 调用带数据源的 SPL 语句之前,需要先在应用项目中添加对应的数据库驱动,然后在配置文件 raqsoftConfig.xml 中配置数据源信息。
例如:调用的 SPL 语句中使用的数据源名称为 demo,数据库类型为 HSQL,那么配置如下:
首先,将 HSQL 的数据集驱动 hsqldb.jar 加载到应用项目中;
其次,在 raqsoftConfig.Xml 的 < Runtime ></ Runtime > 节点中配置数据源信息:
<DBList>
<DB name="demo"> <!--数据源名称-->
<property name="url" value="jdbc:hsqldb:hsql://127.0.0.1/demo" ></property> <!-- url连接-->
<property name="driver" value="org.hsqldb.jdbcDriver" ></property> <!--数据库驱动-->
<property name="type" value="13" ></property> <!--数据库类型-->
<property name="user" value="sa" ></property> <!--用户名-->
<property name="password" value=""></property> <!--密码-->
<property name="batchSize" value="1000" ></property>
<property name="autoConnect" value="true" ></property><!--是否自动连接,如果设定为true,则可以直接以$开头的SQL语句来访问数据库,如果为false,则不会自动连接,使用前必须用connect(db)语句创建数据库连接-->
<property name="useSchema" value="false" ></property>
<property name="addTilde" value="false" ></property>
<property name="dbCharset" value="UTF-8" ></property>
<property name="clientCharset" value="UTF-8" ></property>
<property name="needTransContent" value="false" ></property>
<property name="needTransSentence" value="false" ></property>
<property name="caseSentence" value="false" ></property>
</DB>
</DBList><DBList><DB name="demo"> <!--数据源名称--><property name="url" value="jdbc:hsqldb:hsql://127.0.0.1/demo" /> <!-- url连接--><property name="driver" value="org.hsqldb.jdbcDriver" /> <!--数据库驱动--><property name="type" value="13" /> <!--数据库类型--><property name="user" value="sa" /> <!--用户名--><property name="password" value=""/> <!--密码--><property name="batchSize" value="1000" /><property name="autoConnect" value="true" /><!--是否自动连接,如果设定为true,则可以直接以$开头的SQL语句来访问数据库,如果为false,则不会自动连接,使用前必须用connect(db)语句创建数据库连接--><property name="useSchema" value="false" /><property name="addTilde" value="false" /><property name="dbCharset" value="UTF-8" /><property name="clientCharset" value="UTF-8" /><property name="needTransContent" value="false" /><property name="needTransSentence" value="false" /><property name="caseSentence" value="false" /></DB></DBList>
现在我们通过 SPL 从 demo 数据源中查询 SALES 表,过滤出 SELLERID 为 3 的员工,在 2014 年 11 月 11 号到 2014 年 12 月 12 号期间的所有订单信息:
PreparedStatement pst = con.prepareStatement("$(demo)select * from SALES where SELLERID = ? and ORDERDATE>? and ORDERDATE<?");
//设置参数
pst.setObject(1,"3");
pst.setObject(2,java.sql.Date.valueOf("2014-11-11"));
pst.setObject(3,java.sql.Date.valueOf("2014-12-12"));
//获取结果集
ResultSet rs = pst.executeQuery(); PreparedStatement pst = con.prepareStatement("$(demo)select * from SALES where SELLERID = ? and ORDERDATE>? and ORDERDATE<?"); //设置参数
pst.setObject(1,"3");
pst.setObject(2,java.sql.Date.valueOf("2014-11-11"));
pst.setObject(3,java.sql.Date.valueOf("2014-12-12")); //获取结果集
ResultSet rs = pst.executeQuery();
结果集输出如下:
执行 SPL 脚本
JAVA 集成集算器 JDBC 后,除了可以直接执行单句的 SPL 语句,还可以调用更复杂的 SPL 脚本(后缀为 dfx 的文件)。
比如下面的 dfx 文件:
A | B | C |
1 | =demo.query("select NAME as CITY, STATEID as STATE from CITIES") | [] |
2 | for A1 | =demo.query("select * from STATES where STATEID=?",A2.STATE) |
3 | if left(B2.ABBR,1)==arg1 | >A2.STATE=B2.NAME |
4 | >B1=B1|A2 | |
5 | return B1 |
SPL 脚本思路:
循环遍历 CITIES 表记录,通过 CITIES. STATES 过滤 STATES 表,若 STATES. ABBR 首字母为参数 arg1,则将 STATES 表中的 NAME 值赋给 CITIES. STATE,并将 CITIES 表中的这条记录拼接到 B1 格中,最终返回 B1 格的结果集。
在这个网格文件中,需要从数据源 demo 中获取数据,同时使用了参数 arg1:
数据源配置方法可以参考上面的示例,网格文件保存为 city.dfx,dfx 文件可以存放在应用项目类路径或 raqsoftConfig.xml 中配置的主目录下,当 dfx 文件比较多的时候,为了便于统一维护和管理,我们还可以将 dfx 文件放到 dfx 寻址路径中,寻址路径的配置方式如下:
在 raqsoftConfig.xml 文件的 < Esproc></ Esproc> 节点中,添加以下内容:
<dfxPathList>
<!--配置dfx文件寻址路径,该路径为绝对路径,可以设置多个路径,以“;”隔开-->
<dfxPath>D:\dfxFile</dfxPath>
</dfxPathList><dfxPathList>
<!--配置dfx文件寻址路径,该路径为绝对路径,可以设置多个路径,以“;”隔开-->
<dfxPath>D:\dfxFile</dfxPath></dfxPathList>
//通过call调用存储过程,其中city是dfx的文件名,?表示参数,多个参数间用逗号间隔
st =con.prepareCall("call city(?)");
st.setObject(1, "A");
//获取结果集
ResultSet rs = st.executeQuery(); //通过call调用存储过程,其中city是dfx的文件名,?表示参数,多个参数间用逗号间隔
st =con.prepareCall("call city(?)");
st.setObject(1, "A"); //获取结果集
ResultSet rs = st.executeQuery();
通过 call 调用 dfx 文件除了上面的写法,还可以在调用 dfx 时直接传参,如下:
st=con.prepareStatement("call city(\"A\")");
//获取结果集
ResultSet rs = st.executeQuery(); st=con.prepareStatement("call city(\"A\")"); //获取结果集
ResultSet rs = st.executeQuery();
在 Java 中调用 dfx 文件时,也可以省略 call 直接使用 dfx (…),该用法同样有两种写法,如下:
//dfx名称与参数间以空格作为分隔,多个参数间通过逗号分隔
st =con.prepareCall("city ?");
//设置参数
st.setObject(1,"A"); //dfx名称与参数间以空格作为分隔,多个参数间通过逗号分隔
st =con.prepareCall("city ?"); //设置参数
st.setObject(1,"A");
或:
st =con.prepareCall("city \"A\""); st =con.prepareCall("city \"A\"");
以上几种调用方法最终获取的结果集均相同。
上述内容就是 Java 调用 SPL 脚本的常用方式了,其他程序调用 SPL 的用法: