引言
Lotus Domino 是一种文档型数据库系统,它本身所具有的独特特点,在处理非结构化的数据上,有一种得天独厚的优势。在政府,企事业等单位中办公自动化系统的流程处理的开发上,有着广泛的应用。但是,在处理海量的结构化数据,和功能强大而又简洁的 SQL 相比,Lotus Domino 又显得有些不足。在具有一定规模的 IT 环境中,多种应用系统并用的情况并不少见,像 Lotus Domino 和关系型数据库并存的情况,可以说是经常遇到的事情。在这种情况下,Lotus Domino 和关系型数据库打交道则难以避免。Lotus Domino 提供了对关系型数据库访问的接口,例如 LEI,DECS 等,但是这类接口对 Lotus Domino 开发者来说比较麻烦,而且也不够灵活性。Lotus Domino 还有功能强大的 LotusScript 语言,提供了一种外部数据访问接口 LSPO,它可以通过 ODBC 很方便的访问外部关系型数据库,能够对关系型数据库中的数字,字符串,时间等数据类型进行操作。但是,LSO 不足之处是,它没有提供对大对象数据类型的支持,无法直接操作关系型数据库中 Blob,Clob 等类型的数据。
但是,在开发环境中,如果遇到需要将关系型数据库 Blob 中存储的对象读取出来,转储到 Lotus Domino 的富文本域(简称 RTF)这样的问题,如何进行处理呢?下面,就来介绍一下如何来处理这种问题。众所周知,所有的关系型数据库,几乎都提供了 jdbc 驱动接口,这些 jdbc 接口都有对该数据库中各种类型数据的操作。由于 Lotus Domino 提供对 Java 无缝的支持,因此可以通过 Java 程序将 Blob 类型数据读取出来,然后存放到 Lotus Domino 的 RTF 中。
[size=1.166em]下面提供两种实现的思路:一是在 Lotus Domino 创建一个 Java Agent 去读取数据库中的 Blob 类型数据,再写入导入到 Lotus Domino 的 RTF 中,最后用 Domino Fomula 或者 LotusScript 去调用执行这个 Java agent;二是在 Lotus Domino 写一个 Java Class 完成读取数据库中的 Blob 类型数据,然后用 LotusScript 通过 LS2J 接口去调用 Java Class,并在 LotusScript 中将数据导入到 Lotus Domino 的 RTF 中。
[size=1.166em]LS2J 是 LotusScript 中的一种接口,它允许数据从 Java 数据类型到 LotusScript 数据类型的转换,并允许 LotusScript 去调用 Java 对象方法,使得 LotusScipt 和 Java 两种程序代码很容易的结合在一起使用。
[size=1.166em]现在,我们在 Lotus Domino 8 和 DB2 9.7 上来完成这个工作。
系统环境
[size=1.166em]Lotus Domino 环境:
[size=1.166em]版本:Lotus Domino 8(至少安装 Domino Designer)
[size=1.166em]操作系统:Windows XP professional
[size=1.166em]IP:9.115.77.207
[size=1.166em]关系型数据库环境:
[size=1.166em]版本:DB2 9.7
[size=1.166em]操作系统:Red Hat Enterprise Linux Server release 5.5 (Tikanga)
[size=1.166em]IP:9.115.74.153
[size=1.166em]DB2 的侦听端口:50000。
配置 DB2 环境
[size=1.166em]我们要实现从 DB2 的 Blob 中读取数据,因此在 DB2 中需要创建一个含有 Blob 类型列的表,在插入的一条记录中,Blob 类型列中存放一个二进制对象。为了方便起见,我们可以利用 DB2 样例数据库 Sample 中现有的一个带有 Blob 类型列的表。
[size=1.166em]在 Red Hat Linux 上,切换用户为 DB2 的实例用户 db2inst1,执行命令 db2,进入 DB2 交互式命令行,执行命令 list db directory,在该实例下列出的所有数据库中查看是否存在一个名字为 Sample 的数据库,如果没有,说明在安装 DB2 时,没有同时创建 Sample 数据库。那么,先退出 DB2 交互式命令行,在 Shell 命令行中输入 db2sampl,去创建 Sample 数据库。创建完成之后,再次执行 DB2 命令 list db directory,此时,可以看到如下信息:
db2 => list db directory Database 1 entry: Database alias = SAMPLE Database name = SAMPLE Local database directory = /home/db2inst1 Database release level = d.00 Comment = Directory entry type = Indirect Catalog database partition number = 0 Alternate server hostname = Alternate server port number = db2 =>
[size=1.166em]说明 DB2 的例子数据库 SAMPLE 已经创建好了。下面,在 DB2 交互式命令行中执行如下命令:
db2 => connect to sample Database Connection Information Database server = DB2/LINUX 9.7.0 SQL authorization ID = DB2INST1 Local database alias = SAMPLE db2 => list tables Table/View Schema Type Creation time ------------------------------- --------------- ----- -------------------------- ACT DB2INST1 T 2012-03-28-09.24.49.774304 ADEFUSR DB2INST1 S 2012-03-28-09.24.51.038087 CATALOG DB2INST1 T 2012-03-28-09.24.53.548478 CL_SCHED DB2INST1 T 2012-03-28-09.24.49.185643 CUSTOMER DB2INST1 T 2012-03-28-09.24.53.350539 DEPARTMENT DB2INST1 T 2012-03-28-09.24.49.264115 DEPT DB2INST1 A 2012-03-28-09.24.49.364038 EMP DB2INST1 A 2012-03-28-09.24.49.454047 EMPACT DB2INST1 A 2012-03-28-09.24.49.773634 EMPLOYEE DB2INST1 T 2012-03-28-09.24.49.364688 EMPMDC DB2INST1 T 2012-03-28-09.24.51.761749 EMPPROJACT DB2INST1 T 2012-03-28-09.24.49.740107 EMP_ACT DB2INST1 A 2012-03-28-09.24.49.774010 EMP_PHOTO DB2INST1 T 2012-03-28-09.24.49.454376 ... ... 47 record(s) selected.
[size=1.166em]在列出的一系列表中,我们可以找到一个名称为 EMP_PHOTO。用 describe 查看一下表结构,可以看到第三列为 blob 类型。
db2 => describe table emp_photo Data type Column Column name schema Data type name Length Scale Nulls ------------------------------- --------- ------------------- ---------- ----- ------ EMPNO SYSIBM CHARACTER 6 0 No PHOTO_FORMAT SYSIBM VARCHAR 10 0 No PICTURE SYSIBM BLOB 102400 0 Yes 3 record(s) selected.
[size=1.166em]用数据库的 DML 语句 select 去查看一下该表中的数据。
db2 => select empno,photo_format from emp_photo EMPNO PHOTO_FORMAT ------ ------------ 000130 bitmap 000130 gif 000140 bitmap 000140 gif 000150 bitmap 000150 gif 000190 bitmap 000190 gif 8 record(s) selected. db2 =>
[size=1.166em]从上面可以看出,Sample 数据库中的表 emp_photo 是一个存放员工照片的 Table,每个员工编号对应了 BMP 和 gif 两种类型的照片。下面,我们就来实现把一个员工的 BMP 格式的照片读取出来,存放并显示在 Lotus Domino 一个文档的 RTF 中。
配置 Lotus Domino 环境
[size=1.166em]如果需要让 Lotus Domino 通过 Java 访问 DB2 中的数据,需要为 Lotus Domino 配置 DB2 的 jdbc 驱动程序包。在 Lotus Notes 的配置文件 Notes.ini 中,添加如下参数:
[size=1.166em]JavaUserClasses=.;D:\java\db2java.zip;D:\java\db2jcc.jar;D:\java\db2jcc_license_cu.jar
[size=1.166em]之后,我们在 Lotus Notes 上创建一个 Domino 数据库,本例中数据库名称为 java.nsf。在 java.nsf 数据库中,我们创建一个表单,Form 名称为 Body,在表单上创建一个 RTF 域,名称也为 Body,通过该表单创建的文档来存放员工照片。
转储 DB2 中的 BLOB 类型数据到 Domino 的 RTF 中
[size=1.166em]方法一,在 Domino Designer 中,选择“Share Codes”->“Agents”,去创建一个 Java Agent,在该 Agent 的属性中,将“运行时”的“目标”的值改为“None”。整个程序的逻辑在一个 Java Agent 中完成,该程序实现了如下功能:
连接远程主机上的 DB2 的 Sample 数据库
根据员工号 000190 和图片类型 bitmap,从 emp_photo 表中查询出一条包含 Binary 数据的记录。
将 Blob 列中的数据用文件流的方式读到本地磁盘的文件中。
Domino 在将本地磁盘上的文件以附件的方式存放到一个文档的 RTF 中。
清单 1. 使用 Java Agent 实现 import lotus.domino.*; import java.io.*; import java.lang.*; import java.util.*; import java.sql.*; public class JavaAgent extends AgentBase { public void NotesMain() { try { Session session = getSession(); AgentContext agentContext = session.getAgentContext(); // (Your code goes here) Connection con = null; String url = "jdbc:db2://9.115.74.153:50000/SAMPLE"; String user = "db2inst1"; String password = "password"; String indexstr = "000190"; Database db = agentContext.getCurrentDatabase(); try{ Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance(); con = DriverManager.getConnection(url,user,password); PreparedStatement preparedStatement = con.prepareStatement( "SELECT PICTURE FROM EMP_PHOTO WHERE EMPNO = ? AND PHOTO_FORMAT = ?"); preparedStatement.setString(1,indexstr); preparedStatement.setString(2,"bitmap"); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { Blob blob = resultSet.getBlob(1); InputStream inputStream = blob.getBinaryStream(); File fileOutput = new File("c:\\\\" + indexstr + ".bmp"); FileOutputStream fileos = new FileOutputStream(fileOutput); int ch; while ((ch = inputStream.read()) != -1) fileos.write(ch); fileos.close(); System.out.println("Transferring blob data is complete."); } resultSet.close(); preparedStatement.close(); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(con != null){ con.close(); } }catch(Exception e){ e.printStackTrace(); } } Document doc = db.createDocument(); doc.replaceItemValue("Form", "Body"); RichTextItem body = doc.createRichTextItem("Body"); body.embedObject( EmbeddedObject.EMBED_ATTACHMENT,null, "c:\\" + indexstr +".bmp",null); // Save the document doc.save(true, true); } catch(Exception e) { e.printStackTrace(); } } }
[size=1.166em]在 Agent 列表中,可以在该 Java Agent 上点击右键,在弹出式窗口中选择“Run”去执行,或者在 Formula 公式或者 LotusScript 中调用执行。最后,得到的结果如下图 1 所示:
图 1. 运行 Agent
[size=1.166em]员工的照片是以附件的格式存储在 Lotus 数据库的文档的 RTF 中,浏览时,点击该附件,在弹出式窗口中点击"View"即可。
[size=1.166em]方法二,在 Domino Designer 中,选择“Share Code”->“Script Libaries”,点击“New Java Library”,创建一个 Java Lib。在 Lib 中创建一个 Java Class 完成用从 DB2 中读取 Blob 数据,然后在 LotusScript 中用 LS2J 去调用 Java Class,并完成将图片导入的 RTF 中去。
[size=1.166em]Java Class 实现了如下功能:
连接远程主机上的 DB2 的 Sample 数据库
根据外部传进来的参数和图片类型 bitmap,从 emp_photo 表中查询出一条包含 Binary 数据的记录
将 Blob 列中的数据用文件流的方法读到本地磁盘的文件中。
清单 2. 使用 Java Class 实现 import java.io.*; import java.lang.*; import java.util.*; import java.sql.*; public class getblob{ public void getBlobObject(String indexstr){ Connection con = null; String url = "jdbc:db2://9.115.74.153:50000/SAMPLE"; String user = "db2inst1"; String password = "password"; try{ Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance(); con = DriverManager.getConnection(url,user,password); PreparedStatement preparedStatement = con.prepareStatement( "SELECT PICTURE FROM EMP_PHOTO WHERE EMPNO = ? AND PHOTO_FORMAT = ?"); preparedStatement.setString(1,indexstr); preparedStatement.setString(2,"bitmap"); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { Blob blob = resultSet.getBlob(1); InputStream inputStream = blob.getBinaryStream(); File fileOutput = new File("c:\\\\" + indexstr + ".bmp"); FileOutputStream fileos = new FileOutputStream(fileOutput); int ch; while ((ch = inputStream.read()) != -1) fileos.write(ch); fileos.close(); System.out.println("Transferring blob data is complete."); } resultSet.close(); preparedStatement.close(); }catch(Exception e){ e.printStackTrace(); }finally{ try{ if(con != null){ con.close(); } }catch(Exception e){ e.printStackTrace(); } } } }
[size=1.166em]我们可以再创建一个表单,Form 名称为 CallJava,在表单上创建一个按钮,在其 Click 事件中添加如下代码:
[size=1.166em]主要实现的功能:
LotusScript 使用 LS2J 接口,调用 Java Class,并传入一个员工编号 000140 的参数。
在 Java Class 将 emp_photo 中的 binary 数据读写到本地磁盘后,LotusScript 将数据 import 到一个文档的 RTF 中。
清单 3. 使用 LotusScript 调用 Java Class Sub Click(Source As Button) Dim workspace As New NotesUIWorkspace Dim uidoc As NotesUIDocument Dim mySession As JavaSession Dim myClass As JavaClass Dim getblob As JavaObject Dim indexstr As String Set mySession = New JavaSession() Set myClass = mySession.GetClass("getblob") Set getblob = myClass.CreateObject() indexstr = "000140" getblob.getBlobObject(indexstr) Set uidoc = workspace.ComposeDocument( "", "", "Body" ) Call uidoc.GoToField( "Body" ) Call uidoc.Import("BMP Image", "c:\" + indexstr + ".bmp") Call uidoc.Save End Sub
[size=1.166em]最后,得到的结果如下图 2 所示:
图 2. 员工照片
易错环节检查
在将 DB2 中的 Blob 类型数据转储到 Lotus Domino 的 RTF 的过程中,有几个比较容易出现问题的几个环节。如果出现问题,可以首先去检查一下如下几个方面的情况:
查看 DB2 的 instance 是否启动,侦听端口是否起来,在 Linux 中,DB2 默认的端口一般是 50000,需要查看一下是否改为其他端口,Sample 数据库是否创建,是否能正常的连接。
查看 Lotus Notes 的配置文件 Notes.ini 中是否正确地配置了 DB2 的 jdbc 驱动包,Lotus Notes 的 Java 环境是否正常。
如果 java 程序运行不正常,查看程序是否导入了 sql 类,添加一些调试信息去调试一下。或者也可以直接在 Java 环境中去编译运行一下,将复杂的环境分割成简单的环境来逐步的排查问题。
结束语
上面介绍了在 Lotus Domino 中使用 java 程序来提取 DB2 中 blob 数据的两种方法,可以说这两种方法各有优劣。Java Agent 的整个程序逻辑是由 java 来完成,这在调试和维护方面比较简单,方便。而使用 LS2J 来调用 java class 的方法,则可以使我们使用功能强大的 LotusScript,能用 LotusScript 中前端类来更灵活的处理问题,我们可以将图片在 RTF 中存储为附件格式,也可以 import 进来,将图片直接在 RTF 中展开。因为 java 中没有提供这种功能,所以只能将图片存为附件格式。
[size=1.166em]在所有的数据库中,如 DB2,Oracle,MySQL,PostgreSQL,甚至是 MSSQL Server,都提供 jdbc 驱动程序接口,它们都包含了对关系型数据库中所有数据类型的操作。而 Lotus Domino 则提供了对 Java 程序的无缝支持,利用 java 程序来实现 Lotus Domino 和关系型数据库之间各种数据的转储,不失为一种非常好的方式。