Infosys Technologies Limited
2003 年 9 月 典型的决策支持系统定期载入和插入大量数据。大规模决策支持系统一般都部署在分区环境中。缓冲插入是由 DB2 提供的一种编程技术,使在分区环境中插入密集型工作负载能达到最佳的性能。本文讨论如何在 Java 中利用此功能。还将分析在不同的配置(象变化的行的长度和提交频率)下使用此功能的性能增益。
引言
典型的决策支持系统(decision support system,DSS)定期载入和插入大量数据。大规模 DSS 一般都部署于分区环境中。由于 Java™ 编程语言的普及,本文向您描述可以如何通过使用 Java 来利用 DB2® Universal Database™ 的缓冲插入选项,以使在分区环境中插入密集型(insert-intensive)工作负载达到最佳的性能。
本文假定您多少熟悉一些 Java、SQLJ 和 JDBC。请注意,本文中说明的概念只在 DB2 UDB EEE(即支持数据库分区的 UDB 版本)上有效。
比较 JDBC 和 SQLJ
JDBC 技术是一种 API,让您通过 Java 编程语言能实质地访问任何表格式数据源。它提供到大量 SQL 数据库的跨 DBMS 连通性。JDBC API 允许开发者利用 Java 平台的“一次编写,随处运行(Write Once, Run Anywhere)”的能力,以开发工业强度的、需要访问企业数据的跨平台应用程序。
SQLJ 是一种在 Java 中编写 SQL 访问的 ANSI 标准方法。SQLJ 比 JDBC 更加简明,因而更易于编写,并为更简单的调试提供了编译时模式验证和语法检查。在此方案中,SQL 语句嵌入在 Java 程序代码中,并且用 SQLJ 预编译程序将嵌入式 SQL 语句转换为 Java 方法调用。然而,由 SQLJ 预编译程序生成的代码不可能总是最佳的。关于 SQLJ 的更多信息,请参阅 SQLJ 教程。
分区数据库的能力
因为数据是跨数据库分区划分的,所以利用多个物理节点上多个处理器的能力来满足用户的请求成为可能。数据检索和更新请求都自动地分散到子请求中,并且在适用的数据库分区中并行地执行。对于发出 SQL 语句的应用程序,数据库被分区为多个数据库分区的事实是透明的。
应用程序交互作用发生在一个数据库分区(称为该应用程序的协调程序节点 - coordinator node)中。协调程序与应用程序运行在同一数据库分区上,或者如果是远程应用程序,则协调程序运行在应用程序连接到的数据库分区上。任何数据库分区都可以充当协调程序节点。关于分区数据库的更多信息,请参阅 Clustering for Scalability。
DB2 提供了一些特殊的编程功能以在分区环境中达到最佳的性能。缓冲插入(buffered inserts)便是一项技术的示例,该技术可以使决策支持系统中典型的插入密集型工作负载产生显著的性能增益。
本文中,我们将:
- 详细地讨论缓冲插入。
- 编写一个基于 SQLJ 的表接口类。为简单起见,该类将只支持向表插入数据的接口。
- 说明重复使用 RTStatement 对象的技术如何提高性能。
- 通过以变化的提交频率重复使用 RTStatement,来对 JDBC、基本 SQLJ 和 SQLJ 的性能进行基准测量。我们还将分析行的长度对缓冲插入的影响。
缓冲插入概述
在分区环境中,DB2 UDB 可以利用表队列来缓冲正在插入的行。在类似 DSS 的插入密集型应用程序中,该功能可以使性能显著地提高。要使用缓冲插入,应用程序必须是已准备好的或被绑定并启用了 INSERT BUF 选项的。下面将讨论简单插入和缓冲插入的差别。
分区数据库中的简单插入
图 1中显示的是分区环境中简单数据库插入(无缓冲)的事件流。
图 1. 分区环境中的简单插入处理
- 协调代理程序(Coordinator Agent)从客户机(Client)应用程序中接收行。
- 协调程序将其传送到该节点上的数据库管理器(Database Manager)。
- 数据库管理器在分区键值上应用散列逻辑并确定目标节点(Target Node)。
- 目标节点接收该行并在本地将其插入。
- 目标节点将响应发回协调代理程序。
- 协调程序将响应传送回客户机。
分区数据库中的缓冲插入
如 图 2所示,如果该应用程序被绑定并启用了 INSERT BUF 选项,事件流会稍有不同。
图 2. 分区环境中的缓冲插入
- 数据库管理器为该表所在的每个节点打开一个 4 KB 的页面。
- 在从协调代理程序接收该行之后,数据库管理器应用散列逻辑并确定目标节点。
- 然后将该行放置到对应的页面中。
- 数据库管理器将控制返回应用程序。
- 如果该连接只用来向同一表中插入行,则当缓冲区充满时或者在遇到 commit 或 rollback 语句时,缓冲区会清空。通常,除了 insert 语句外,向此表中插入任何其它语句都会使缓冲区清空。示例将会插入到另一张表、从表中删除、更新表等。
请访问 DB2 UDB 7.2 Application Development Guide -- Chapter 18: Programming Considerations in a Partitioned Environment以获取会清空缓冲区的语句的完整列表。
缓冲插入带来更好的性能是由于:
- 从目标节点为每个由目标接收的缓冲区发送单个消息到协调程序节点,以减少节点间的通信开销。
- 在其它节点上进行插入的同时,允许协调程序节点接收新的行。
要利用缓冲插入,您必须将应用程序绑定并启用 INSERT BUF 选项。在 JDBC 中,SQL 语句会在运行时动态地绑定。由于这种局限性,基于 JDBC 编写的代码无法使用缓冲插入。然而,在基于 SQLJ 的数据库代码中使用该功能是可能的。
SQLJ 和缓冲插入 - 示例
让我们拿一个 Java 程序作为示例,该程序向定义如下的表中插入行:
create table testtable (
id integer not null,
name char(20) not null,
age smallint not null,
address char(74) not null )
partitioning key (id);
alter table testtable add primary key(id);
|
名为 id 的那一列既是表的主键,也是表的分区键。
我们将实现此代码来使用两个类向这张表插入数据:
清单 2中显示的类实现测试表的插入操作。
清单 2. 用于插入操作的 TestTableInserter.sqlj
// TestTableInserter.sqlj
import java.sql.*;
/**
* Implements the method to insert a row into testtable
*/
public class TestTableInserter {
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ref.DefaultContext _defaultContext;
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ExecutionContext _executionContext ;
/**
* Sole Constructor. Sets the protected DefaultContext variable and initializes the
* ExecutionContext to be used for the insert statement.
* @param connCtx DefaultContext object used for SQLJ operations
*/
public TestTableInserter( sqlj.runtime.ref.DefaultContext connCtx) {
_defaultContext = connCtx;
// This instance of ExecutionContext will be used in the insert operation
_executionContext = new sqlj.runtime.ExecutionContext( );
}
/**
* Inserts a single row into the testtable.
* @param row TestTableRow object containing data to be inserted
* @throws SQLException when database error occurs
*/
public void insertRow(TestTableRow row) throws SQLException {
int id = row.id;
String name = row.name;
short age = row.age;
String address = row.address;
#sql [_defaultContext,_executionContext] { insert into testtable values
(:id, :name, :age , :address) };
}
}
|
在 TestTableInserter.sqlj 上运行 SQLJ 转换程序(sqllib\bin\sqlj.exe)。这样就用 Java 源代码语句替换了 SQLJ 程序中的 SQL 语句,并生成一个序列化概要文件,该文件包含关于 SQLJ 源文件中 SQL 语句的信息。
附录 A中显示了由转换程序生成的代码。
在缺省情况下,转换程序除创建此 java 文件之外,还执行以下步骤:
- 编译 TestTableInserter.java 以生成两个类文件:TestTableInserter.class 和 TestTableInserter_SJProfileKeys.class。
- 生成 TestTableInserter_SJProfile0.ser,它包含关于嵌入在原始源代码中的 SQL 语句的概要文件信息。
对于 SQL 处理的概述,请参阅 John Campbell 关于此课题的 Meet the Experts主题。
现在,运行 db2profc 命令以在生成的概要文件上安装 DB2 SQLJ 定制器并在目标 DB2 数据库中创建 DB2 程序包。
db2profc -user=dbuser -password=dbpwd -url=jdbc:db2:dbname -prepoptions=
"bindfile using TestTableInserter.bnd package using TTblIns insert buf"
TestTableInserter_SJProfile0.ser
|
注:insert buf 选项(它被指定为 db2profc 命令的预选项属性的一部分)使得此 insert 语句成为缓冲插入的候选。此选项的缺省值是 insert def,它禁用缓冲插入。
通过重复使用 RTStatement 对象进行优化
由转换程序生成的代码用以下 清单 3中所示的代码片段替换了嵌入式插入语句。
清单 3. 由 SQLJ 转换程序生成的代码
1 {
2 sqlj.runtime.ConnectionContext __sJT_connCtx = _defaultContext;
3 if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX();
4 sqlj.runtime.ExecutionContext __sJT_execCtx = _executionContext;
5 if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX();
6 int __sJT_1 = id;
7 String __sJT_2 = name;
8 short __sJT_3 = age;
9 String __sJT_4 = address;
10 synchronized (__sJT_execCtx) {
11 sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement
12 (__sJT_connCtx, TestTableInserter_SJProfileKeys.getKey(0), 0);
13 try
14 {
15 __sJT_stmt.setInt(1, __sJT_1);
16 __sJT_stmt.setString(2, __sJT_2);
17 __sJT_stmt.setShort(3, __sJT_3);
18 __sJT_stmt.setString(4, __sJT_4);
19 __sJT_execCtx.executeUpdate();
20 }
21 finally
22 {
23 __sJT_execCtx.releaseStatement();
24 }
25 }
26 }
|
从 清单 3明显可知,生成的代码为每个对 insertRow 函数的调用创建(行号:11)并破坏(行号:23)RTStatement 类的实例。这样开销太大。如 附录 B 中的 SQLJStmtCachedTestTableInserter 类所示,我修改了代码来除去这些开销,步骤如下:
- 添加一个受保护的变量(_insertStatement)来存储 RTStatement 的实例。
- 向构造函数添加“throws SQLException”子句,因为对 registerStatement 的调用可能抛出 SQLException。
- 将 insertRow 方法中的 registerStatement 调用传送到 TestTableInserter 的构造器。
- 现在 insertRow 方法使用 RTStatement 的受保护的实例。现在 RTStatement 对象只在构造函数中创建一次并可用于任何数量的 insertRow 调用,而不必在每次调用 insertRow 时都创建新的 RTStatement 对象。
- insertRow 方法允许抛出 SQLException。另外,因为 _insertStatement 是这个类的实例变量,所以无需在每次调用后破坏 RTStatement 类的实例。因此,在 insertRow 方法中除去 try 和 finally 块。
- 添加一个 finalize 方法以在垃圾收集的过程中释放 RTStatement 对象。
基准测量结果
对以下方案分析使用缓冲插入的性能增益。
- 行的长度对缓冲插入的影响。
- 提交频率对缓冲插入的影响。
图 3中所示的部署配置用于执行不同的测试用例。
图 3. 基准测量的配置
在全部测试用例方案中,客户机应用程序连接到 Node 1,即测试数据库的目录节点(Catalog Node)。另外,分析中使用的测试数据在两个节点之间几乎平等地分布。
涉及的不同机器的配置如 表 1所示。
| 表 1:机器配置 |
| 系统 |
OS |
CPU |
内存 |
软件 |
| Node 1 - 目录 |
Win NT 4.0 (SP5) |
Pentium III 647 |
196 MB |
IBM DB2 UDB EEE 7.2 |
| Node 2 |
Win NT 4.0 (SP5) |
Pentium III 647 MHz |
196 MB |
IBM DB2 UDB EEE 7.2 |
| 客户机 |
Win 2000 Professional |
Pentium III 800 MHz |
128 MB |
IBM DB2 UDB EEE 7.2 客户机 |
SQLJStmtCachedTestTableInserter(重复使用的 RTStatement)和 TestTableInserter 类用于分析行的长度对缓冲插入的影响。客户机应用程序用于向测试表中插入 10000 行。随着地址列的数据类型的变化,行的大小在 0.1 KB 到 4 KB 之间变化。在测试程序结尾的单个提交用于将提交对缓冲插入的影响减到最小。在启用和禁用缓冲插入两种情况下都分别进行测试。对于不同的行的长度,也测量了使用普通 JDBC 代码的插入性能。结果如 图 4所示。
图 4. 行的大小对缓冲插入的影响
结果显示了使用缓冲插入的性能优势。对于长度较小的行,用缓冲插入每秒插入行的数量较之简单插入高出好多倍。随着行的长度的增加,由缓冲插入产生的性能增益迅速减小。这是因为,缓冲区在其清空之前所能保留的行数随着行的大小的增加变得更小。
如此前所讨论的那样,commit 语句(及其它一些语句)会导致缓冲插入中使用的缓冲区清空。因此随着提交频率的增加,由缓冲产生的性能增益减小。 图 5 比较了当使用以下代码时提交频率的影响:
- JDBC,如 附录 C中所示。
- 基本 SQLJ 代码,如 附录 A中所示。
- 优化 SQLJ 代码,如 附录 B中所示。
附录 D中的清单显示了用于测试两个 SQLJ 变体的测试程序。
图 5. 提交频率对缓冲插入的影响
结果显示,随着提交频率的增加和行的大小的增大,使用缓冲插入的性能优势减小。
两个测试都在双节点的配置中进行。请注意,随着我们增加数据库节点的数量,性能会几乎呈线形提高。
局限性
在设计使用缓冲插入的应用程序时,必须对此功能的以下局限性给予应有的考虑。
- 缓冲插入技术仅对于优化向表的频繁插入有效。对于涉及在一系列表上混合有插入、更新和删除操作的典型 QLTP 工作负载,使用此技术是不可取的。
- 在插入一组行的过程中检测出的错误将会导致该组的全部行回退。一组行的定义是从以下条件开始的使用缓冲插入语句插入的所有行:
- 从工作单元的开头
- 自从该语句(如果是动态的)编译好,或
- 自从另一条关闭或清空缓冲插入的语句的前一次执行。
- 由于它们的异步性质,缓冲插入会表现出某些影响应用程序的行为。某些错误状态可能无法作为插入本身的一部分进行报告。它们可以在关闭缓冲插入语句的任何语句中进行报告。例如,在发出 commit 语句时您会收到一个代码为 -803 的 SQL 错误(唯一键违规)。
- 使用游标通过 SELECT 语句将不会立即看到插入的行。如果一个应用程序正在使用缓冲插入,则它不应该依赖游标选定的(cursor-selected)行。
结束语
在分区环境中,使用缓冲插入可能达到非常高的插入性能。要使用 Java 来利用此功能,代码必须基于 SQLJ。它对于向同一表执行插入密集型操作的应用程序很有用。对于行的长度很小的表,性能增益是很显著的。
支持文件
| 描述 |
文件类型 |
文件大小 |
下载方法 |
| bufferedInsertSource.zip |
zip |
10 KB |
HTTP 下载 |
参考资料
- Adamache, Blair. 发表在 DB2 开发者园地上的 Clustering for Scalability。
- 在 http://www-4.ibm.com/cgi-bin/db2www/data/db2/udb/winos2unix/support/v7pubs.d2w/en_main上有 DB2 UDB 7.2 Application Development Guide - Chapter 18: Programming Considerations in a Partitioned Environment。
- 在 http://www.sqlj.org上有 SQLJ 教程。
致谢
作者想要感谢 Infosys 的 Srinivas Thonse,感谢他对本文初稿细致的技术检查和他的鼓励;感谢 IBM 的 Cass Squire 和 Glen Sheffield,他们提出了许多深思熟虑的评论以及为发表此文章提供了指导;还要感谢 Kathy Zeidenstein 细致的编辑检查。
附录 A
下面列出的是由 sqlj 转换程序生成的代码:
/*@lineinfo:filename=TestTableInserter*//*@lineinfo:user-code*//*@lineinfo:1^1*/// TestTableInserter.sqlj
import java.sql.*;
/**
* Implements the method to insert a row into testtable
*/
public class TestTableInserter {
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ref.DefaultContext _defaultContext;
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ExecutionContext _executionContext ;
/**
* Sole Constructor. Sets the protected DefaultContext variable and initializes the
* ExecutionContext to be used for the insert statement.
* @param connCtx DefaultContext object used for SQLJ operations
*/
public TestTableInserter( sqlj.runtime.ref.DefaultContext connCtx) {
_defaultContext = connCtx;
// This instance of ExecutionContext will be used in the insert operation
_executionContext = new sqlj.runtime.ExecutionContext( );
}
/**
* Inserts a single row into the testtable.
* @param row TestTableRow object containing data to be inserted
* @throws SQLException when database error occurs
*/
public void insertRow(TestTableRow row) throws SQLException {
int id = row.id;
String name = row.name;
short age = row.age;
String address = row.address;
/*@lineinfo:generated-code*//*@lineinfo:40^4*/
// ************************************************************
// #sql [_defaultContext,_executionContext] { insert into testtable values (:id, :name, :age , :address) };
// ************************************************************
{
sqlj.runtime.ConnectionContext __sJT_connCtx = _defaultContext;
if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX();
sqlj.runtime.ExecutionContext __sJT_execCtx = _executionContext;
if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX();
int __sJT_1 = id;
String __sJT_2 = name;
short __sJT_3 = age;
String __sJT_4 = address;
synchronized (__sJT_execCtx) {
sqlj.runtime.profile.RTStatement __sJT_stmt = __sJT_execCtx.registerStatement
(__sJT_connCtx, TestTableInserter_SJProfileKeys.getKey(0), 0);
try
{
__sJT_stmt.setInt(1, __sJT_1);
__sJT_stmt.setString(2, __sJT_2);
__sJT_stmt.setShort(3, __sJT_3);
__sJT_stmt.setString(4, __sJT_4);
__sJT_execCtx.executeUpdate();
}
finally
{
__sJT_execCtx.releaseStatement();
}
}
}
// ************************************************************
/*@lineinfo:user-code*//*@lineinfo:40^106*/
}
}/*@lineinfo:generated-code*/class TestTableInserter_SJProfileKeys
{
private static TestTableInserter_SJProfileKeys inst = null;
public static java.lang.Object getKey(int keyNum)
throws java.sql.SQLException
{
if (inst == null)
{
inst = new TestTableInserter_SJProfileKeys();
}
return inst.keys[keyNum];
}
private final sqlj.runtime.profile.Loader loader = sqlj.runtime.RuntimeContext.getRuntime().getLoaderForClass(getClass());
private java.lang.Object[] keys;
private TestTableInserter_SJProfileKeys()
throws java.sql.SQLException
{
keys = new java.lang.Object[1];
keys[0] = sqlj.runtime.ref.DefaultContext.getProfileKey(loader, "TestTableInserter_SJProfile0");
}
}
|
附录 B
下面列出的是性能分析中使用的修改过的代码:
/*@lineinfo:filename=SQLJStmtCachedTestTableInserter*//*@lineinfo:user-code*//*@lineinfo:1^1*/// TestTableInserter.sqlj
import java.sql.*;
/**
* Implements the method to insert a row into testtable
*/
public class SQLJStmtCachedTestTableInserter {
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ref.DefaultContext _defaultContext;
/**
* Stores the DefaultContext object used in the insert statement
*/
protected sqlj.runtime.ExecutionContext _executionContext ;
/**
* Stores the registered insert statement
*/
protected sqlj.runtime.profile.RTStatement _insertStatement ;
/**
* Releases the RTStatement object during garbage collection
* @throws SQLException if releaseStatement call fails
*/
protected void finalize() throws SQLException {
_executionContext.releaseStatement();
}
/**
* Sole Constructor. Sets the protected DefaultContext variable and initializes the
* ExecutionContext to be used for the insert statement.
* @param connCtx DefaultContext object used for SQLJ operations
* @throws SQLException if registerStatement call fails
*/
public SQLJStmtCachedTestTableInserter( sqlj.runtime.ref.DefaultContext connCtx) throws SQLException {
_defaultContext = connCtx;
// This instance of ExecutionContext will be used in the insert operation
_executionContext = new sqlj.runtime.ExecutionContext( );
_insertStatement = _executionContext.registerStatement(_defaultContext, TestTableInserter_SJProfileKeys.getKey(0), 0);
}
/**
* Inserts a single row into the testtable.
* @param row TestTableRow object containing data to be inserted
* @throws SQLException when database error occurs
*/
public void insertRow(TestTableRow row) throws SQLException {
int id = row.id;
String name = row.name;
short age = row.age;
String address = row.address;
/*@lineinfo:generated-code*//*@lineinfo:40^4*/
// ************************************************************
// #sql [_defaultContext,_executionContext] { insert into testtable values (:id, :name, :age , :address) };
// ************************************************************
{
sqlj.runtime.ConnectionContext __sJT_connCtx = _defaultContext;
if (__sJT_connCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX();
sqlj.runtime.ExecutionContext __sJT_execCtx = _executionContext;
if (__sJT_execCtx == null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_EXEC_CTX();
int __sJT_1 = id;
String __sJT_2 = name;
short __sJT_3 = age;
String __sJT_4 = address;
synchronized (__sJT_execCtx) {
_insertStatement.setInt(1, __sJT_1);
_insertStatement.setString(2, __sJT_2);
_insertStatement.setShort(3, __sJT_3);
_insertStatement.setString(4, __sJT_4);
__sJT_execCtx.executeUpdate();
}
}
// ************************************************************
/*@lineinfo:user-code*//*@lineinfo:40^106*/
}
}/*@lineinfo:generated-code*/class TestTableInserter_SJProfileKeys
{
private static TestTableInserter_SJProfileKeys inst = null;
public static java.lang.Object getKey(int keyNum)
throws java.sql.SQLException
{
if (inst == null)
{
inst = new TestTableInserter_SJProfileKeys();
}
return inst.keys[keyNum];
}
private final sqlj.runtime.profile.Loader loader = sqlj.runtime.RuntimeContext.getRuntime().getLoaderForClass(getClass());
private java.lang.Object[] keys;
private TestTableInserter_SJProfileKeys()
throws java.sql.SQLException
{
keys = new java.lang.Object[1];
keys[0] = sqlj.runtime.ref.DefaultContext.getProfileKey(loader, "TestTableInserter_SJProfile0");
}
}
|
附录 C
import java.sql.*;
import java.util.*;
class JDBCTableInserterTester
{
static
{
try
{
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception
{
String url = "jdbc:db2:testdb";
Properties prop = new Properties();
prop.put("user", "user");
prop.put("password", "pwd");
Connection con = null;
int commitFrequency ;
if (args.length ==0 )
commitFrequency = 10000;
else
commitFrequency = Integer.parseInt(args[0]);
try
{
// connect with user id/password
con = DriverManager.getConnection(url, prop);
con.setAutoCommit(false);
}
catch (SQLException e)
{
System.out.println("Error: could not open connection to database");
System.err.println(e) ;
System.exit(1);
}
System.out.println("Starting to insert : " + new java.util.Date() ) ;
PreparedStatement inserter = con.prepareStatement("insert into testtable values(?,?,?,?)");;
String name = "myname";
short age = 27;
char[] arr = new char[74];
Arrays.fill(arr, 'a');
String address = new String(arr);
for ( int id = 0; id < 10000; id++ ) {
inserter.setInt(1, id);
inserter.setString(2,name);
inserter.setShort(3,age);
inserter.setString(4, address);
inserter.executeUpdate();
if ((id + 1) % commitFrequency == 0) con.commit();
}
con.commit();
System.out.println("Finished inserting : " + new java.util.Date() ) ;
}
}
|
附录 D
import java.sql.*;
import java.util.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
class SQLJTableInserterTester
{
static
{
try
{
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception
{
String url = "jdbc:db2:testdb";
Properties prop = new Properties();
prop.put("user", "user");
prop.put("password", "pwd");
int commitFrequency ;
if (args.length ==0 )
commitFrequency = 10000;
else
commitFrequency = Integer.parseInt(args[0]);
Connection con = null;
DefaultContext ctx = null;
try
{
// connect with default id/password
con = DriverManager.getConnection(url, prop);
con.setAutoCommit(false);
ctx = new DefaultContext(con);
}
catch (SQLException e)
{
System.out.println("Error: could not get a default context");
System.err.println(e) ;
System.exit(1);
}
System.out.println("Starting to insert : " + new java.util.Date() ) ;
TestTableRow row = new TestTableRow();
TestTableInserter inserter = new TestTableInserter(ctx);
row.name = "myname";
row.age = 27;
char[] arr = new char[74];
Arrays.fill(arr, 'a');
row.address = new String(arr);
for ( int id = 0; id < 10000; id++ ) {
row.id = id;
inserter.insertRow(row);
if ( (id + 1) % commitFrequency == 0 ) con.commit();
}
con.commit();
System.out.println("Finished inserting : " + new java.util.Date() ) ;
}
}
|
关于作者
Kishnakumar Pooloth 是 Infosys Technologies Limited( http://www.infy.com)的一位技术专家。他的专业领域包括对象设计、组件技术、数据库优化以及用 J2EE 进行企业级应用程序的开发。在 DB2 UDB 应用程序开发方面,他是 IBM 认证的解决方案专家。他在印度的 Calicut University 获得了电子学和通信(Electronics and Communication)的学士学位。您可以通过 krishnakumarp@infy.com与他联系。 |
|