WEB开发网
开发学院软件开发Java 使用 Jython 连接 Apache Derby 数据库 阅读

使用 Jython 连接 Apache Derby 数据库

 2010-04-22 00:00:00 来源:WEB开发网   
核心提示:概述Apache Derby 是一种用 100% 纯 Java™ 编写的关系数据库,该项目最初被称作 Cloudscape ™,使用 Jython 连接 Apache Derby 数据库,后来 IBM® 将它捐献给了 Apache 软件基金组织,该项目目前正处在孵化(incubation

概述

Apache Derby 是一种用 100% 纯 Java™ 编写的关系数据库。该项目最初被称作 Cloudscape ™,后来 IBM® 将它捐献给了 Apache 软件基金组织,该项目目前正处在孵化(incubation)阶段。这意味着我们有一种易用的关系数据库可以使用,并且,在拥有 Apache Software License 的情况下,这种关系数据库是完全免费的。

Jython 是 Python 编程语言的 100% 纯 Java 实现。如果将 Jython 与 Derby 结合使用,我们便拥有了编写高级的、动态的、面向对象的脚本所需的工具,而这种脚本与 Java 平台是无缝集成的。本文将向您展示这一切要完成起来有多容易。

本文无意成为 Jython、Apache Derby 或 SQL 的介绍性文档。实际上,它将展示如何使用现有的、免费提供的技术 —— Jython,通过两种不同的方法来操纵 Derby 关系数据库。

准备工作

遵循下面的步骤,在 Windows XP® 系统上配置一个简单的环境。

表 1: 安装/初始化步骤

采取的行动 描述 注释/结果
安装从 ActiveState 下载的 Pythonversion 2.4 将 Python 安装到 C:\Python24
验证安装打开一个命令提示符,然后执行:

...> python -V

结果:

Python 2.4

安装从 developerWorks 下载的 Javaverion 1.4.2 将 Java 安装到 C:\j2sdk1.4.2 之类的目录
验证安装打开一个命令提示符,然后执行:

...> java -Version

结果类似于:

java version "1.4.2"

安装从 Jython.org 下载的 Jythonverion 2.1 将 Jython 安装到 C:\Jython
验证安装打开一个命令提示符,然后执行:

...> C:\Jython\jython.bat --version

结果类似于:

Jython 2.1 on java

定义文件关联在命令提示符下,执行:

...> assoc .jy=Jython.File

定义文件扩展名关联
验证文件关联在命令提示符下,执行:

...> assoc .jy

结果:

.jy=Jython.File

定义文件类型命令在命令提示符下,执行:

...> ftype Jython.File="C:\Jython\jython.bat" "%1" %*

定义如何 "执行" 一个 Jython 命令
验证文件类型命令在命令提示符下,执行:

...> ftype Jython.File

结果:

"C:\Jython\jython.bat" "%1" %*

将 ".jy" 定义为可执行文件扩展名  单击 Start 按钮。

右击 My Computer 条目。

选择 Properties。

选择 Advanced 标签页。

单击 Environment Variables 按钮。

在 "System Variables" 部分,向下滚动并选择 PATHEXT 条目。

单击 Edit 按钮。

添加 ;.jy 到输入文本域的末尾。

单击 OK 按钮关闭 "Edit System Variable" 对话框。

单击 OK 按钮关闭 "Environment Variables" 对话框。

单击 OK 按钮关闭 "System Properties" 对话框

将 ".jy" 添加到 "executable" 文件扩展名列表当中
验证 PATHEXT 值打开一个 new 命令提示符,然后执行:

...> set pathext

结果类似于:

PATHEXT=.com;.exe;.bat;...;.jy

安装 Apache Derby 文件  浏览至 Apache Derby Project 页面。

选择 Downloads 链接。

选择 Official Release 下载链接。

选择适当的下载项(例如,incubating-derby-10.0.2.1-bin.zip)。

对下载的文件执行病毒扫描。

解压下载的文件。

将展开后的目录结构复制或移动到 C:\Derby

将 Derby 文件安装到 C:\Derby
可选: 安装 IBM Cloudscape 文件  访问 IBM 网站。

在搜索栏的文本域中输入 Cloudscape 。

单击 Search 按钮。

单击 IBM Cloudscape 10.0 -- Support 链接。

单击 Trial software 链接。

单击适当的 Released Product: IBM Cloudscape (Version 10.0, ... Install) 链接。

如果还没有合法的用户 ID 和密码,那么注册一下。

下载安装文件和说明文件。

对下载的文件执行病毒扫描。

按照安装说明安装该软件(例如,安装到 C:\Cloudscape)

将 Cloudscape 文件安装到 C:\Cloudscape

为什么安装 Cloudscape?

为什么同时安装 IBM Cloudscape 和 Apache Derby,这里有很多原因。我发现最重要的原因是,IBM Cloudscape 包括:

PDF 格式的文档(更易于搜索)

示例程序

示例数据库

用于执行各个实用程序的命令文件,例如:

dblookData Definition Language (DDL) Generation Utility (也称作模式转储工具)
ij用于在数据库上运行脚本的交互式 JDBC 脚本工具
sysinfo提供关于 Cloudscape 版本和系统的信息的实用程序
*NetworkServer*.bat用于启动和停止作为网络服务器的 Cloudscape 的命令文件

lsPath.jy —— 一个 Jython 脚本

这个脚本没有使用 Derby,但是它将帮助您验证之前的安装情况,并且在后面的过程中它仍是有用的。我发现阅读像 PATH 和 CLASSPATH 这样的环境变量值比较困难,因为它们包含了很多的项(即目录,可能还有文件名),各项之间用一个定界符隔开。于是我编写了 lsPath 脚本,以便将各个项分行显示。

我能够将这个脚本编写成平台独立的,也就是说,它可以同时在 UNIX® 和 Windows® 系统上运行。

不幸的是,像 lsPath.jy 这样的脚本在 Windows 上有一个很小的令人讨厌的地方。如果我们想在 UNIX 上重定向输出,那么只需使用比较直观的命令即可,例如:

...> lsPath >outputfile

但是这样的语法在 Windows 上却不能通过,因为 Windows 命令解释器(即 cmd.exe)处理文件-类型关联(就像我们在 表 1 中定义的)重定向的方式有所不同。不过,变通的方法还是有的。我们可以不使用我们定义的用于调用脚本的速记符,而是输出完整的命令:

...> C:\Jython\Jython lsPath.jy >outputfile

如果包含 Jython.bat (例如 C:\Jython)的目录已经放在 PATH 环境变量中,那么就更简单了:

...> Jython lsPath.jy >outputfile

清单 1: lsPath.jy

"""lsPath - Display delimited environment variables in an easy to read format""" 
from os import environ, path, pathsep 
from sys import argv 
Name = path.split( argv[ 0 ] )[ 1 ] 
if not Name : Name = "lsPath.jy" 
Usage = "Usage: jython " + Name + " [Path-Environment-Variable]" 
def main( EnvVar = None ) : 
 """lsPath() 
Display Environment variable values in an easier to read format""" 
 if EnvVar == None : 
  EnvVar = "PATH" 
 else : 
  EnvVar = EnvVar.upper() 
 try : 
  Path = environ[ EnvVar ].split( pathsep ) 
  print EnvVar + "=" 
  for dir in Path : 
   print " " + dir 
 except : 
  print "Unknown ENV variable: " + EnvVar 
  
if __name__ == "__main__" : 
 if len( argv ) == 1 : 
  main() 
 elif len( argv ) == 2 : 
  main( argv[ 1 ] ) 
 else : 
  print Usage 

通过使用这个脚本,我们可以验证 Jython 和 Java 是否已经成功安装。还请注意,通过这个脚本读取和了解程序的输出,比起用 Windows SET 命令尝试和获得环境变量值要容易多少。

环境变量值

您发现下面这些输出哪个更容易阅读?

很可能,我的眼光比您要落伍一些,但我还是更喜欢 lsPath 的输出。您呢?

图 1: 比较 SET 和 lsPath.jy 的输出

C:\Programs\Jython> set PATH 
Path=C:\Python24;C:\WINDOWS;C:\WINDOWS\system32;C:\WINDOWS\System32\Wbem;C:\PROG 
RA~1\IBM\INFOPR~1;C:\PROGRA~1\ThinkPad\Utilities;C:\PROGRA~1\IBM\TRACEF~1;C:\PRO 
GRA~1\PERSON~1;C:\PROGRA~1\IBM\SQLLIB\BIN;C:\PROGRA~1\IBM\SQLLIB\FUNCTION;C:\j2s 
dk1.4.2_01\bin;C:\Utilities 
PATHEXT=.com;.exe;.bat;.cmd;.py;.pyo;.pyc;.pyw;.jy 
C:\Programs\Jython> lsPath 
PATH= 
 C:\Python24 
 C:\WINDOWS 
 C:\WINDOWS\system32 
 C:\WINDOWS\System32\Wbem 
 C:\PROGRA~1\IBM\INFOPR~1 
 C:\PROGRA~1\ThinkPad\Utilities 
 C:\PROGRA~1\IBM\TRACEF~1 
 C:\PROGRA~1\PERSON~1 
 C:\PROGRA~1\IBM\SQLLIB\BIN 
 C:\PROGRA~1\IBM\SQLLIB\FUNCTION 
 C:\j2sdk1.4.2_01\bin 
 C:\Utilities 
C:\Programs\Jython> lsPath pathext 
PATHEXT= 
 .com 
 .exe 
 .bat 
 .cmd 
 .py 
 .pyo 
 .pyc 
 .pyw 
 .jy 
C:\Programs\Jython> set classpath 
CLASSPATH=.;C:\PROGRA~1\IBM\SQLLIB\java\db2java.zip;C:\PROGRA~1\IBM\SQLLIB\java\ 
db2jcc.jar;C:\PROGRA~1\IBM\SQLLIB\java\sqlj.zip;C:\PROGRA~1\IBM\SQLLIB\bin;C:\PR 
OGRA~1\IBM\SQLLIB\java\common.jar 
C:\Programs\Jython> lsPath classpath 
CLASSPATH= 
 . 
 C:\PROGRA~1\IBM\SQLLIB\java\db2java.zip 
 C:\PROGRA~1\IBM\SQLLIB\java\db2jcc.jar 
 C:\PROGRA~1\IBM\SQLLIB\java\sqlj.zip 
 C:\PROGRA~1\IBM\SQLLIB\bin 
 C:\PROGRA~1\IBM\SQLLIB\java\common.jar 

启动 Derby Network Server

现在我们有了可以使用的 Jython,接下来我们将目光转向 Apache Derby 数据库。首先,我们看看如何使用 Derby 作为 Network Server,然后我们再看如何使用 嵌入式 Derby 数据库。至于判断是使用嵌入式数据库还是使用网络服务器数据库的一些相关因素和环境,则超出了本文的范围。欲了解这方面的信息,最好先阅读 Derby 文档。

通过使用 Cloudscape™ 脚本文件作为例子,我们可以编写自己的简单的 Derby Server 脚本,用它们来启动和停止 Derby 数据库。为了启动 Derby Network Server 查询,需要调用下面的脚本,并指定 "start" 作为参数。例如: ...> NetworkServerControl start。

Apache Derby 的全限定安装路径最好不要包含空格。如果该路径包含了空格(例如 C:\Program Files\Derby),那么应该使用缩略名(即 C:\PROGRA~1\Derby)或 tilda。请参阅随 Cloudscape 附带的 setNetworkServerCP.bat,看看如何在一个脚本中动态地完成这一点。

TITLE 命令用于更改在窗口标题栏所显示的信息,以便显示窗口中正在执行什么动作。

将 Derby 目录作为当前工作目录的主要原因是确保所有 Derby 数据库都放在这里。

清单 2: NetworkServerControl.bat

@ECHO OFF 
TITLE NetworkServerControl: %* 
setlocal 
set DERBY=C:\Derby 
set LIB=%DERBY%\lib 
set CP=%LIB%\derby.jar;%LIB%\derbytools.jar;%LIB%\derbynet.jar 
cd %DERBY% 
java -cp "%CP%" org.apache.derby.drda.NetworkServerControl %* 
endlocal 
TITLE Command prompt 

停止 Derby Network Server

为了确定 NetworkServerControl 程序有哪些选项,我们调用该程序时不带任何参数。这样做后,我们看到,为了停止 Derby Network Server,我们需要指定 "shutdown" 作为参数。例如: ...> NetworkServerControl shutdown

请注意,当我们启动 Derby Network Server 时,它将继续等待传入的连接请求,直到被停止。因此,我们必须另外打开一个命令窗口,以便调用 NetworkServerControl 程序来执行其他请求。不管是停止或关闭 Derby Network Server,我们都还需要另一个命令窗口。

当我们为脚本指定一个不认识的参数时,我们就会认识到使用被调用的 Java 类(即 NetworkServerControl)来命名批处理(.bat)文件的一个好处。因而,解决这个问题所需的文档将与匹配的类相关联。

图 2: NetworkServerControl —— 使用信息

C:\Derby> NetworkServerControl 
No arguments given. 
Usage: NetworkServerControl <commands> 
Commands: 
start [-h <host>] [-p <portnumber>] 
shutdown [-h <host>][-p <portnumber>] 
ping [-h <host>][-p <portnumber>] 
sysinfo [-h <host>][-p <portnumber>] 
runtimeinfo [-h <host>][-p <portnumber>] 
logconnections {on|off}[-h <host>][-p <portnumber>] 
maxthreads <max>[-h <host>][-p <portnumber>] 
timeslice <milliseconds>[-h <host>][-p <portnumber>] 
trace {on|off} [-s <session id>][-h <host>][-p <portnumber>] 
tracedirectory <traceDirectory>[-h <host>][-p <portnumber>] 
C:\Derby> NetworkServerControl start 
Server is ready to accept connections on port 1527. 
*** Open a separate command window to execute the following *** 
C:\Derby> NetworkServerControl ping 
Connection obtained for host: localhost, port number 1527. 
C:\Derby> NetworkServerControl shutdown 
Shutdown successful. 

连接到 Derby Network Server

在 图 2 中可以看到,我们不仅可以使用 NetworkServerControl 来让 Derby Network server 等待传入的连接请求,而且还可以用它来证明连接请求是可行的。在连接到 Derby Network Server 之前,Jython 脚本还需要其他一些东西。我们需要一个 JDBC 驱动程序。幸运的是,developerWorks 提供了这样一个驱动程序。

获得 JDBC 驱动程序 zip 文件并将其解压到 Derby 的 lib 目录下之后,我们需要更新 CLASSPATH,使之包含这些驱动程序。

为了更新 CLASSPATH,使用 set 命令。

文件名中的 JCC 代表 Java Common Client。

图 3: 更新后的 CLASSPATH

C:\Derby> lsPath classpath 
CLASSPATH= 
 C:\Derby\lib\derby.jar 
 C:\Derby\lib\derbytools.jar 
 C:\Derby\lib\derbynet.jar 
 C:\Derby\lib\db2jcc.jar 
 C:\Derby\lib\db2jcc_license_c.jar 

使用 ij 实用程序

现在,我们可以使用 Derby 提供的其他实用程序,特别是 ij 实用程序,来做更多的事情,而不只是连接到 Derby Network Server。要想了解如何使用 ij 实用程序,我们同样可以利用 Cloudscape 提供的脚本。除了查看和研究 ij 脚本以外,我还将 Cloudscape\demo\databases\sample 目录复制到了 Derby 目录中。

默认情况下,密码没有通过认证,因此要使用 userid 来定义用于连接的初始模式。由于到数据库的连接只能出现在同一台机器上,因此这样不大会引起安全问题。该模式标识数据库中将通过默认方式访问的表。这样我们便可以使用 "SELECT * FROM ORG",而不是使用 "SELECT * FROM SAMP.ORG"。

注意,JDBC URL 中包含了对 Derby Network Server 的引用,这意味着,在建立连接之前,必须启动 Derby Network Server。

图 4: ij 会话

C:\Derby> java -Dij.user=SAMP -Dij.password=SAMP org.apache.derby.tools.ij 
ij version 10.0 (C) Copyright IBM Corp. 1997, 2004. 
ij> connect 'jdbc:derby:net://localhost:1527/sample'; 
ij> select * from org; 
DEPTN&|DEPTNAME   |MANAG&|DIVISION |LOCATION 
----------------------------------------------------- 
10  |Head Office  |160  |Corporate |New York 
15  |New England  |50  |Eastern  |Boston 
20  |Mid Atlantic |10  |Eastern  |Washington 
38  |South Atlantic|30  |Eastern  |Atlanta 
42  |Great Lakes  |100  |Midwest  |Chicago 
51  |Plains    |140  |Midwest  |Dallas 
66  |Pacific    |270  |Western  |San Francisco 
84  |Mountain   |290  |Western  |Denver 
8 rows selected 
ij> disconnect all; 
ij> exit; 

使用 Jython 交互式地连接到 Derby

让我们用 Jython 来做我们刚才用 ij 实用程序所做的事情。幸运的是,Jython 附带了 zxJDBC 包,这是 "Jython 中用于数据库连接的近乎 100% 遵从 Python DB API 2.0 的接口"。图 5 展示了如何使用这个包连接到 Derby 并与之交互。

您应该可以将这里的每条 Jython 语句与前面图 4 中显示的 ij 会话关联起来。只有一处在 ij 会话中是隐式的,而在这里是显式的,那就是 JDBC driver 的标识。通过将 db2jcc*.jar 文件包括在类路径中,ij 可以使用它们建立连接。

暂时不用担心 cursor.description 元组中所包含的值。稍后我们将更详细地对它们加以讨论。

特别注意:由于某种原因,到数据库的连接默认情况下是禁用 autocommit 的。这意味着,我们必须首先发出 db.commit(),才能成功地用 db.close() 关闭数据库对象。如果您想知道尝试不执行 commit 就关闭数据库会出现什么情况,那么可以参见 图 10,在这里我们展示了使用嵌入式 Derby 的同一个例子。

使用交互式 Jython 会话进行了试验之后,我们可以简单地将适当的语句复制到一个文件,然后便可以通过解释器执行该文件。或者,我们甚至可以编译它们,然后让 Java 虚拟机执行字节代码,而不必每次都解释脚本。

图 5: Jython 会话

C:\Derby> jython 
Jython 2.1 on java1.4.2_01 (JIT: null) 
Type "copyright", "credits" or "license" for more information. 
>>> from com.ziclix.python.sql import zxJDBC 
>>> url = 'jdbc:derby:net://localhost:1527/sample' 
>>> driver = 'com.ibm.db2.jcc.DB2Driver' 
>>> user = password = 'SAMP' 
>>> db = zxJDBC.connect( url, user, password, driver ) 
>>> cursor = db.cursor() 
>>> cursor.execute( 'SELECT * FROM ORG' ) 
>>> for field in cursor.description : 
...  print field 
... 
('DEPTNUMB', 5, 6, None, 5, 0, 0) 
('DEPTNAME', 12, 14, None, None, None, 1) 
('MANAGER', 5, 6, None, 5, 0, 1) 
('DIVISION', 12, 10, None, None, None, 1) 
('LOCATION', 12, 13, None, None, None, 1) 
>>> result = cursor.fetchall() 
>>> for row in result : 
...  print row 
... 
(10, 'Head Office', 160, 'Corporate', 'New York') 
(15, 'New England', 50, 'Eastern', 'Boston') 
(20, 'Mid Atlantic', 10, 'Eastern', 'Washington') 
(38, 'South Atlantic', 30, 'Eastern', 'Atlanta') 
(42, 'Great Lakes', 100, 'Midwest', 'Chicago') 
(51, 'Plains', 140, 'Midwest', 'Dallas') 
(66, 'Pacific', 270, 'Western', 'San Francisco') 
(84, 'Mountain', 290, 'Western', 'Denver') 
>>> print str( len(result) ) + ' rows selected' 
8 rows selected 
>>> cursor.close() 
>>> db.commit() 
>>> db.close() 
>>> ^Z 
*** After placing these same statements into zxORGtable.jy *** 
C:\Derby> zxORGtable 
table field descriptions: 
------------------------ 
('DEPTNUMB', 5, 6, None, 5, 0, 0) 
('DEPTNAME', 12, 14, None, None, None, 1) 
('MANAGER', 5, 6, None, 5, 0, 1) 
('DIVISION', 12, 10, None, None, None, 1) 
('LOCATION', 12, 13, None, None, None, 1) 
ORG tables rows: 
--------------- 
(10, 'Head Office', 160, 'Corporate', 'New York') 
(15, 'New England', 50, 'Eastern', 'Boston') 
(20, 'Mid Atlantic', 10, 'Eastern', 'Washington') 
(38, 'South Atlantic', 30, 'Eastern', 'Atlanta') 
(42, 'Great Lakes', 100, 'Midwest', 'Chicago') 
(51, 'Plains', 140, 'Midwest', 'Dallas') 
(66, 'Pacific', 270, 'Western', 'San Francisco') 
(84, 'Mountain', 290, 'Western', 'Denver') 
8 rows selected 

zxJDBC Cursor description 细节

现在该描述 cursor.description 中的字段内容了,如 图 5 中所示。成功执行一个 SQL 查询的一个结果是:值被赋给每个返回的字段的 description 属性。这使得程序可以确定关于为每个选定行而存在的字段的信息。结果中的每个字段都有一个表,这个表包含了 表 2 中的信息。不幸的是,您可以看到,Datatype 是一个数字,因此使用起来有点笨拙。还好,有一个 Java API 可以处理这些值,您不用知道或关心使用的是什么数字。

表 2: 游标元组内容

字段名字符串字段的全大写名称
DatatypeNumeric来自 java.sql.Types 的值
Display SizeNumeric以字符为单位计算的宽度
Internal SizeNonezxJDBC 包没有提供 —— 总是为 "None"
PrecisionNumeric只为数字类型而定义
ScaleNumeric只为数字类型而定义
isNullableNumeric布尔值: 0 = False; 1 = True

如果我们对数字值不感兴趣,而只对确定如何处理特定类型的数据感兴趣,那么我们可以使用 java.sql 包提供的信息。清单 3 和清单 4 展示了使用该信息的一些方法。

清单 3: SQL 数字类型

import java.sql 
... 
NumTypes = [        
 java.sql.Types.BIGINT , 
 java.sql.Types.DECIMAL , 
 java.sql.Types.DOUBLE , 
 java.sql.Types.FLOAT  , 
 java.sql.Types.INTEGER , 
 java.sql.Types.NUMERIC , 
 java.sql.Types.REAL  , 
 java.sql.Types.SMALLINT, 
 java.sql.Types.TINYINT  
] 
... 
# To test if a specific variable (Type) is numeric, you can simply: 
if Type in NumTypes : 
 # Handle numeric type entries here 
else : 
 # Handle non-numeric type entries here 

如果您有兴趣将一个名称与每个可能的 SQL 数据类型相关联,那么可能需要使用类似清单 4 中所示的内容。

清单 4: 显示数据类型名

        ... 
TypeName = { 
 java.sql.Types.ARRAY    : 'ARRAY'    , 
 java.sql.Types.BIGINT    : 'BIGINT'    , 
 java.sql.Types.BINARY    : 'BINARY'    , 
 java.sql.Types.BIT     : 'BIT'     , 
 java.sql.Types.BLOB     : 'BLOB'     , 
 java.sql.Types.CHAR     : 'CHAR'     , 
 java.sql.Types.CLOB     : 'CLOB'     , 
 java.sql.Types.DATE     : 'DATE'     , 
 java.sql.Types.DECIMAL   : 'DECIMAL'   , 
 java.sql.Types.DISTINCT   : 'DISTINCT'   , 
 java.sql.Types.DOUBLE    : 'DOUBLE'    , 
 java.sql.Types.FLOAT    : 'FLOAT'    , 
 java.sql.Types.INTEGER   : 'INTEGER'   , 
 java.sql.Types.JAVA_OBJECT : 'JAVA_OBJECT' , 
 java.sql.Types.LONGVARBINARY: 'LONGVARBINARY', 
 java.sql.Types.LONGVARCHAR : 'LONGVARCHAR' , 
 java.sql.Types.NULL     : 'NULL'     , 
 java.sql.Types.NUMERIC   : 'NUMERIC'   , 
 java.sql.Types.OTHER    : 'OTHER'    , 
 java.sql.Types.REAL     : 'REAL'     , 
 java.sql.Types.REF     : 'REF'     , 
 java.sql.Types.SMALLINT   : 'SMALLINT'   , 
 java.sql.Types.STRUCT    : 'STRUCT'    , 
 java.sql.Types.TIME     : 'TIME'     , 
 java.sql.Types.TIMESTAMP  : 'TIMESTAMP'  , 
 java.sql.Types.TINYINT   : 'TINYINT'   , 
 java.sql.Types.VARBINARY  : 'VARBINARY'  , 
 java.sql.Types.VARCHAR   : 'VARCHAR'    
} 
... 
# To display the kind of type used, you can simply: 
print str( Type ) + TypeName[ Type ] 

另一种连接技术

在 图 5 中,我们看到如何使用 zxJDBC 包连接到 Derby 数据库。如果您需要编写更符合 java.sql 包定义的 ResultSet 接口的脚本,那么您可能更喜欢图 6 中所示的技术。

眼尖的人会注意到,这些并不完全是前面用 zxJDBC 包所做的东西。然而,暂时来说,这些也已经足够接近了。

在下一节中将提到的 UserTables.jy 中仍可以找到完整的 RSMD 和 printRSMD 例程。

图 6: 另一个 Jython 会话

C:\Derby> jython 
Jython 2.1 on java1.4.2_01 (JIT: null) 
Type "copyright", "credits" or "license" for more information. 
>>> import java.sql 
>>> from RSMD import printRSMD, RSMD 
>>> from java.lang import Class 
>>> from java.util import Properties 
>>> 
>>> NumTypes = [ java.sql.Types.BIGINT, java.sql.Types.DECIMAL, java.sql.Types.D 
OUBLE, java.sql.Types.FLOAT, java.sql.Types.INTEGER, java.sql.Types.NUMERIC, jav 
a.sql.Types.REAL, java.sql.Types.SMALLINT, java.sql.Types.TINYINT ] 
>>> 
>>> url = 'jdbc:derby:net://localhost:1527/sample' 
>>> driver = 'com.ibm.db2.jcc.DB2Driver' 
>>> P = Properties() 
>>> P.setProperty( 'user', 'SAMP' ) 
>>> P.setProperty( 'password', 'SAMP' ) 
>>> jcc = Class.forName( driver ).newInstance() 
>>> conn = java.sql.DriverManager.getConnection( url, P ) 
>>> stmt = conn.createStatement() 
>>> Query = 'SELECT * FROM ORG' 
>>> rs = stmt.executeQuery( Query ) 
>>> rsmd = RSMD( rs, NumTypes ) 
>>> printRSMD( rsmd, Query) 
 Fields contained in: SELECT * FROM ORG 
| Size | Label         |Type |Type Name 
+-------+------------------------+-----+-------------------- 
|   6|DEPTNUMB        |  5|SMALLINT 
|   14|DEPTNAME        |  12|VARCHAR 
|   6|MANAGER         |  5|SMALLINT 
|   10|DIVISION        |  12|VARCHAR 
|   13|LOCATION        |  12|VARCHAR 
>>> 
>>> while ( rs.next() ) : 
...  row = ( rs.getInt( 1 ), rs.getString( 2 ), rs.getInt( 3 ), rs.getString( 4 
 ), rs.getString( 5 ) ) 
...  row 
... 
(10, 'Head Office', 160, 'Corporate', 'New York') 
(15, 'New England', 50, 'Eastern', 'Boston') 
(20, 'Mid Atlantic', 10, 'Eastern', 'Washington') 
(38, 'South Atlantic', 30, 'Eastern', 'Atlanta') 
(42, 'Great Lakes', 100, 'Midwest', 'Chicago') 
(51, 'Plains', 140, 'Midwest', 'Dallas') 
(66, 'Pacific', 270, 'Western', 'San Francisco') 
(84, 'Mountain', 290, 'Western', 'Denver') 
>>> rs.close() 
>>> stmt.close() 
>>> conn.close() 
>>> 
>>> ^Z 
*** After placing these statements into ORGtable.jy *** 
C:\Derby> ORGtable 
 Fields contained in: SELECT * FROM ORG 
| Size | Label         |Type |Type Name 
+-------+------------------------+-----+-------------------- 
|   6|DEPTNUMB        |  5|SMALLINT 
|   14|DEPTNAME        |  12|VARCHAR 
|   6|MANAGER         |  5|SMALLINT 
|   10|DIVISION        |  12|VARCHAR 
|   13|LOCATION        |  12|VARCHAR 
(10, 'Head Office', 160, 'Corporate', 'New York') 
(15, 'New England', 50, 'Eastern', 'Boston') 
(20, 'Mid Atlantic', 10, 'Eastern', 'Washington') 
(38, 'South Atlantic', 30, 'Eastern', 'Atlanta') 
(42, 'Great Lakes', 100, 'Midwest', 'Chicago') 
(51, 'Plains', 140, 'Midwest', 'Dallas') 
(66, 'Pacific', 270, 'Western', 'San Francisco') 
(84, 'Mountain', 290, 'Western', 'Denver') 
8 rows selected 

小结

在探索 Jython 和 Derby 时,我提供了已编写好的两个程序。我想再编写一个程序,用于显示关于某个 Derby 数据库中存在的用户表的信息。于是我编写了同一个程序的 zxJDBC 版本和非 zxJDBC 版本(分别是 zxUserTables.jy 和 UserTables),以便了解编写各个版本时需要涉及哪些东西。这两个程序产生的输出是一样的。输出的开始部分如图 7 所示。

更早的时候我曾提到,在默认情况下,zxJDBC 包是在没有启用 autocommit 的情况下打开数据库连接的。如果查看 zxUserTables.jy 文件,您将发现如何启用 autocommit。成功地连接到数据库之后,只需添加一条 db.autocommit = 1 语句。

图 7: UserTables 程序输出的开始部分

C:\Derby> UserTables 
 Fields contained in: * FROM SYS.SYSTABLES, SYS.SYSSCHEMAS 
| Size | Label         |Type |Type Name 
+-------+------------------------+-----+-------------------- 
|   36|TABLEID         |  1|CHAR 
|  128|TABLENAME        |  12|VARCHAR 
|   1|TABLETYPE        |  1|CHAR 
|   36|SCHEMAID        |  1|CHAR 
|   1|LOCKGRANULARITY     |  1|CHAR 
|   36|SCHEMAID        |  1|CHAR 
|  128|SCHEMANAME       |  12|VARCHAR 
|  128|AUTHORIZATIONID     |  12|VARCHAR 
|TableName 
+-------------------- 
|SAMP.PROJECT 
|SAMP.STAFF 
|SAMP.CL_SCHED 
|SAMP.EMP_ACT 
|SAMP.ORG 
|SAMP.EMP_PHOTO 
|SAMP.IN_TRAY 
|SAMP.SALES 
|SAMP.EMP_RESUME 
|SAMP.EMPLOYEE 
|SAMP.DEPARTMENT 
11 rows selected 
 Fields contained in: SAMP.PROJECT 
| Size | Label         |Type |Type Name 
+-------+------------------------+-----+-------------------- 
|   6|PROJNO         |  1|CHAR 
|   24|PROJNAME        |  12|VARCHAR 
|   3|DEPTNO         |  1|CHAR 
|   6|RESPEMP         |  1|CHAR 
|   7|PRSTAFF         |  3|DECIMAL 
|   10|PRSTDATE        |  91|DATE 
|   10|PRENDATE        |  91|DATE 
|   6|MAJPROJ         |  1|CHAR 
... 

嵌入式 Derby 数据库

有时候 Network Server 可能不是必需的,或者是不适合的。如果您有一个简单的应用程序,或许合理的做法是让 Java 关系数据库包与应用程序在同一个虚拟机中执行。有了 Derby,要做到这一点很容易,使用嵌入式 Derby 数据库与使用 Derby Network 客户机之间仅有的不同之处在于:

CLASSPATH

Driver

Database URL

这是什么意思呢?首先,我们使用一个不同的 CLASSPATH。图 3 展示了为了访问 Derby Network Server 需要包括哪些项。从图 8 中可以看到,我们不再需要 "IBM DB/2 JDBC Universal Driver" 相关 jar 文件。

图 8: 用于嵌入式 Derby 的 CLASSPATH

C:\Derby> lsPath classpath 
CLASSPATH= 
 C:\Cloudscape\lib\derby.jar 
 C:\Cloudscape\lib\derbynet.jar 
 C:\Cloudscape\lib\derbytools.jar 

如 图 4 所示,用于连接到 Derby Network Server 数据库的 ij 会话非常简单。图 9 展示了相同的 ij 会话,不过这次使用的是嵌入式 Derby。

使用 Jython 连接 Apache Derby 数据库
注意,真正不同的只有 URL。这是因为 ij 实用程序可以根据 URL 确定适当的驱动程序。

图 9: 使用嵌入式 Derby 的 ij 会话

C:\Derby> java -Dij.user=SAMP -Dij.password=SAMP org.apache.derby.tools.ij 
ij version 10.0 (C) Copyright IBM Corp. 1997, 2004. 
ij> connect 'jdbc:derby:sample'; 
ij> select * from org; 
DEPTN&|DEPTNAME   |MANAG&|DIVISION |LOCATION 
----------------------------------------------------- 
10  |Head Office  |160  |Corporate |New York 
15  |New England  |50  |Eastern  |Boston 
20  |Mid Atlantic |10  |Eastern  |Washington 
38  |South Atlantic|30  |Eastern  |Atlanta 
42  |Great Lakes  |100  |Midwest  |Chicago 
51  |Plains    |140  |Midwest  |Dallas 
66  |Pacific    |270  |Western  |San Francisco 
84  |Mountain   |290  |Western  |Denver 
8 rows selected 
ij> disconnect all; 
ij> exit; 

就像前面看到的一样,ij 根据 URL 得出应该使用哪种驱动程序。对于 Jython 代码,从图 10 中可以看到,我们必须显式地为特定的 URL 标识适当的驱动程序。

如前所述,这与我们在 图 5 中所看到的内容之间仅有的不同之处就在于 URL 和 Driver 值。

还应注意的是,在默认环境(autocommit 被禁用)中,当试图不发出 db.commit() 命令就关闭数据库时,会出现什么情况。

图 10: 使用 zxJDBC 和嵌入式 Derby 的 Jython 会话

C:\Derby> jython 
Jython 2.1 on java1.4.2_01 (JIT: null) 
Type "copyright", "credits" or "license" for more information. 
>>> from com.ziclix.python.sql import zxJDBC 
>>> url = 'jdbc:derby:sample' 
>>> driver = 'org.apache.derby.jdbc.EmbeddedDriver' 
>>> user = password = 'SAMP' 
>>> db = zxJDBC.connect( url, user, password, driver ) 
>>> cursor = db.cursor() 
>>> cursor.execute( 'SELECT * FROM ORG' ) 
>>> for field in cursor.description : 
...  print field 
... 
('DEPTNUMB', 5, 6, None, 5, 0, 0) 
('DEPTNAME', 12, 14, None, None, None, 1) 
('MANAGER', 5, 6, None, 5, 0, 1) 
('DIVISION', 12, 10, None, None, None, 1) 
('LOCATION', 12, 13, None, None, None, 1) 
>>> result = cursor.fetchall() 
>>> for row in result : 
...  print row 
... 
(10, 'Head Office', 160, 'Corporate', 'New York') 
(15, 'New England', 50, 'Eastern', 'Boston') 
(20, 'Mid Atlantic', 10, 'Eastern', 'Washington') 
(38, 'South Atlantic', 30, 'Eastern', 'Atlanta') 
(42, 'Great Lakes', 100, 'Midwest', 'Chicago') 
(51, 'Plains', 140, 'Midwest', 'Dallas') 
(66, 'Pacific', 270, 'Western', 'San Francisco') 
(84, 'Mountain', 290, 'Western', 'Denver') 
>>> print str( len( result ) ) + ' rows selected' 
8 rows selected 
>>> cursor.close() 
>>> db.close() 
Traceback (innermost last): 
 File "<console>", line 1, in ? 
Error: Invalid transaction state. [SQLCode: 20000], [SQLState: 25000] 
>>> db.commit() 
>>> db.close() 
>>> ^Z 

结束语

在本文中我们看到,我们有一种易用的、免费的关系数据库可供使用。我们还看到,我们可以用 Jython 轻松方便地开发高级的、动态的、面向对象的程序,而该程序与 Java 平台是无缝集成的。此外,我们还看到,编写连接到 Derby Network Server 的脚本,或者使用与应用程序在同一个虚拟机中执行的嵌入式 Derby 服务器有多容易。

Tags:使用 Jython 连接

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接