iBATIS(OR)映射框架的使用

iBATIS 是什么?

这一节将描述 iBATIS 中的单独的 API,以及为什么您可能使用它们,并了解 iBATIS 优于其他数据库映射框架的优点。

iBATIS 框架

简言之,iBATIS 由两个单独的框架组成。可以将 Data Mapper 框架专门用于 OR 映射,OR 映射是 Java 域对象到数据库中关系表的映射。DAO 框架为应用程序提供了一个简洁一致的访问基础数据的方法。

iBATIS Data Mapper 框架 (Data Mapper)

Data Mapper 是执行 SQL 并将结果映射回对象的框架,它使您不必手工执行此操作。

Data Mapper 框架不要求使用任何特殊版本的 Java 对象。您不必实现任何接口或生成任何代码,不必为其他一些基本对象创建子类或遵循任何奇怪的惯例,也不必学习特定于该框架的辅助查询语言。

可以使用一个简单并直接的 XML 格式来定义 iBATIS 将 Java 对象映射到数据库的方式。可以直接用 SQL 定义所需的具体查询,并有选择地使用任何特定于正使用的数据库引擎的专有 SQL。此功能允许您使用您想要的方式来映射对象和执行连接。

iBATIS Data Access Objects 框架(DAO 框架)

DAO 框架的主要目标是抽象化应用程序的数据访问层和持久层的表示方式位置,使它们远离应用程序的业务逻辑。DAO 框架允许在应用程序中定义负责数据中心操作的接口。

例如,如果应用程序使用直接的 Java Database Connectivity (JDBC) 来获得持久性,则 DAO 框架的目标是抽象这些类和接口(比如 ConnectionPreparedStatementResultSet)的使用,使它们远离应用程序,并下移到持久层中。

如果应用程序出于某种原因使用 HTTP GET 和 POST 来获得和存储数据,则 DAO 框架的用途变成抽象化类(比如 HttpUrlConnection)的使用,使它们远离应用程序的业务层。然后应用程序可以使用 DAO 接口在数据上执行操作,这些接口的实现被抽象化,远离业务逻辑。这些实现可以从数据库、Web 服务或其他任何源中获得数据。

DAO 框架不依赖于 Data Mapper 框架的使用。您可以选择在一个项目中同时使用这两个框架(成对使用它们相当不错),或者也可以单独使用每个框架。这一教程系列将展示单独使用框架和一起使用框架的好处。






iBATIS 的优点

iBATIS 优于其他一些 OR 映射工具的优点是:

  • iBATIS 没有使用它自己的专用查询语言,它只使用 SQL。一些 OR 映射工具(比如 Hibernate)除了使用 SQL 之外还使用它们自己的查询语言。
  • 所有想要执行的查询和更新都是使用 SQL 编写的(并存储在 .xml 文件中)。一些人可能认为这是一个缺点,因此想把数据库从他们那里完全抽离出来,以避免需要写入任何 SQL 代码。这也正是许多开发人员喜欢 Hibernate 的一个原因。但您可能更喜欢能够在访问对象时更好地控制具体将执行哪种 SQL,而不是愿意以某种依赖于基础 OR 映射框架的方式未曾预料地为您生成它。您可以根据数据库管理员 (DBA) 的建议,或者根据关系数据库管理系统 (RDBMS) 中提供的工具所提供的访问计划或查询优化器来调优查询或其他语句。直接访问为这一层编写的 SQL 的另一个优点是可以利用数据库所提供的任何专用 SQL。
  • iBATIS 易于使用。
  • 该项目有大量文件可以证明。
  • 它没有外部依赖性。其他一些 OR 映射框架随同装载了 15 到 20 个 .jar 文件,并依赖于这些文件的特定版本来运行框架。您不需要或者也不想在开发应用程序时遇到这类头疼问题,因此使用没有任何外部依赖性的 iBATIS 实际上是一大优点。(注意,一些可选配置允许使用外部连接工具或字节代码增强之类的东西,但这些并不是必需的。)

现在是时间深入研究一些更特殊的 iBATIS 概念和语义,这些最终会将引导我们到一些编码和示例。


BATIS Data Mapper 的语义

本教程的剩余部分以近乎专有的方式查看 Data Mapper 框架(第 2 部分将深入介绍 DAO 框架)。这一小节将介绍 Data Mapper 的语义。

Mapped Statement

Data Mapper 的核心功能是围绕 Mapped Statement 进行的。Mapped Statement 可以拥有称为 Parameter Map(基本上用于数据输入)和 Result Map(数据输出)的框架。因此 Mapped Statement 实质上是一个 XML 元素,该元素包含负责执行某些操作并将输入/输出参数映射到 Java 对象的 SQL 语句。清单 3 显示了一个简单的来自 JPetStore 演示版的 SQL Mapped Statement(请参阅参考资料,获得到该下载的链接)。


清单 3. 一个简单的 SQL Mapped Statement
<select id="getUsernameList" 
resultClass="string"
parameterClass="account">
select USERNAME as value from SIGNON
</select>

清单 3 中的 Mapped Statement 负责查询 SIGNON 表中的 USERNAME 列的所有值。有几种不同类型的 Mapped Statement。正如您所看到的,此特殊 Mapped Statement 是一个 <select>。除了 <select> 之外,还可以在使用 iBATIS 框架时使用 <statement><insert><update><delete><procedure> Mapped Statement 元素。iBATIS 文档更详细地介绍了每个元素(请参阅参考资料,获得到 iBATIS 的 Web 站点的链接)。






Parameter Map 和内联参数

iBATIS 框架中的 Parameter Map 为 Mapped Statement 提供数据输入参数。Parameter Map 不常被使用并且是自发地使用(通常使用内联参数),但清单 4 显示了一个它们如何工作的示例,其中有一个来自文档的示例 Parameter Map 和 Mapped Statement。


清单 4. iBATIS 框架中的 Parameter Map
<parameterMap id="insert-product-param" class="com.domain.Product">
<parameter property="id" jdbcType="NUMERIC"
javaType="int" nullValue="-9999999"/>
<parameter property="description" jdbcType="VARCHAR"
nullValue="NO_ENTRY"/>
</parameterMap>

<statement id="insertProduct" parameterMap="insert-product-param">
insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);
</statement>

您可以看到,清单 4 中的 Mapped Statement 根据名称引用 Parameter Map,它包含两个占位符问号。(您将认识这些占位符,以 JDBC PreparedStatement 的标准占位符的形式。)它将从 Parameter Map 中获得的值按它们被定义的顺序应用于这些占位符。

清单 4 中的 Parameter Map 定义了 com.domain.Product 类的 id 属性 getId(),将它映射到使用此 Parameter Map 的任何 Mapped Statement 的第一个占位符(问号)。它继续(使用下一个参数元素)声明 com.domain.Product 类的 description 属性 getDescription(),将它映射到使用 Parameter Map 的任何 Mapped Statement 中的第二个占位符(问号)。在 parameterMap 中,parameter 元素的显示顺序与将它们应用于使用 parameterMap 的 Mapped Statement 中的占位符问号的顺序相同。

更常见的是使用内联参数映射输入参数(参见清单 5)。


清单 5. 内联参数
<statement id="insertProduct" parameterClass="com.domain.Product">
insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
values (#id#, #description#);
</statement>

此语法使用 com.domain.Product 类中的 getId() 返回的值替换 #id#,而 #description#com.domain.Product 类的 getDescription() 返回的值替换。您可以查看 iBATIS 文档,了解如何指定 null 值。






Result Map

Result Map 类似于 Parameter Map,但它用于输出。Result Map 允许您定义将 Mapped Statements(通常是一些查询)映射回 Java 对象的方式。清单 6 提供了来自 iBATIS 文档的一个示例的快速查看。


清单 6. Result Map
<resultMap id="get-product-result" class="com.domain.Product">
<result property="id" column="PRD_ID"/>
<result property="description" column="PRD_DESCRIPTION"/>
</resultMap>

<statement id="getProduct" resultMap="get-product-result">
select * from PRODUCT
</statement>

您可以看到,具有 getProductid 的 Mapped Statement 明确地引用 Result Map 的授权 get-product-result,告诉 Mapped Statement 将 PRD_ID 数据库列映射到 com.domain.Product 类的 JAVAid 属性,还声明将 PRD_DESCRIPTION 数据库列映射到 com.domain.Product 类的 JAVAdescription 属性。

我总是喜欢指定我将要选择的具体列,而不是使用(比如说)SELECT * FROM






TransactionManager

Data Mapper 框架中的 TransactionManager 元素允许在给定配置的情况下按您希望的对事务服务进行配置。此元素的当前受支持类型是:

  • JDBC - JDBC 事务管理器通过 java.sql.Connection 接口的 commit()rollback() 方法内部控制事务。
  • JTA - 使用一个全局 Java Transaction API (JTA) 事务,并要求 UserTransaction 通过 Java Naming and Directory Interface (JNDI) 或其他任何方法变得可用。
  • EXTERNAL - 用户可以自己管理事务。对于必须自己以任何方式管理所有事务的非事务性数据而言,这是一个不错的选择。

此教程系列的第 3 部分将更详细地查看这些事务。

配置 Derby 和 iBATIS

这一节将介绍设置基本 Derby 和 iBATIS 配置并使其运行需要做的所有事情。(正如前面所提到的,本教程将介绍 Data Mapper 框架并保存 Data Access Object 配置,将它们用于第 2 部分。)

JAR 文件

将 Apache Derby 和 iBATIS 放在一起使用的最重要的一件事是减少依赖性。在这里,您所需要的东西是运行 Derby 的 derby.jar 文件,以及 ibatis-common-2.jar 和 ibatis-sqlmap-2.jar 文件。(如果正在使用 DAO 框架,那么还需要 ibatis-dao-2.jar 文件,但本教程中没有介绍该文件。)

注意,如果想利用 iBATIS 的一些额外的功能,比如字节代码增强或集中式/分布式缓存,那么还需要包含 iBATIS 使用的库(在这里分别是 CGLIB 和 OS Cache)。这些额外的组件通常是不必要的。






配置文件

iBATIS 很少要求设置配置文件并运行它们。Data Mapper 框架需要一个 XML 配置文件(通常称为 sql-map-config.xml),该文件定义了与事务管理有关的项,以及如何连接到数据库。指定包含 Mapped Statements、Result Map 等事项的 .xml 文件列表也是在这里进行的。快速浏览一下将在本教程的简单示例中使用的 sql-map-config.xml 文件(参见清单 7)。


清单 7. sql-map-config.xml
<!DOCTYPE sqlMapConfig PUBLIC
"-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

<properties resource="properties/database.properties"/>

<settings cacheModelsEnabled="true" enhancementEnabled="false"
maxSessions="64" maxTransactions="8" maxRequests="128"/>

<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property value="${driver}" name="JDBC.Driver"/>
<property value="${url}" name="JDBC.ConnectionURL"/>
<property value="${username}" name="JDBC.Username"/>
<property value="${password}" name="JDBC.Password"/>
<property value="15" name="Pool.MaximumActiveConnections"/>
<property value="15" name="Pool.MaximumIdleConnections"/>
<property value="1000" name="Pool.MaximumWait"/>
</dataSource>
</transactionManager>

<sqlMap resource="net/humandoing/invoicing/domain/sql/Sequence.xml"/>
<sqlMap resource="net/humandoing/invoicing/domain/sql/Product.xml"/>
</sqlMapConfig>

清单 7 中,只有一个描述映射的 .xml 文件,在这里该文件是 Product.xml。通常,将在几个文件中定义此信息,然后每个域对象大致分到一个 .xml 文件。此外,要注意 XML 文件引用数据库属性文件的方式,该文件包含如何连接到数据库的信息。清单 8 显示了一个示例 database.properties 文件。


清单 8. 一个 database.properties 文件
####################################
# Database Connectivity Properties
####################################

driver=org.apache.derby.jdbc.EmbeddedDriver
url=jdbc:derby:c:/temp/ibatis
username=
password=


在这里,只提供了 iBATIS 的嵌入式 Derby 驱动程序的完全限定名称以及一个有效的 JDBC URL。不需要指定用户名和口令,因为在这个示例中没有涉及安全性。

除了清单 7 中的配置文件外,Data Mapper 框架惟一需要的是任何一种为描述数据库查询以及对象与数据库之间的映射方式而定义的 .xml 文件。接下来的小节将讨论用于示例应用程序的 Product.xml 文件。

尽管在本教程中不会使用到,但 DAO 框架仍然需要一个简单的 .xml 配置文件(通常称为 dao.xml),并包含一个项列表,这些项类似于 Data Mapper 配置中的那些项。这一节中包含一些事务管理信息以及 DAO 接口与实现所组成的对的列表(本教程系列的第 2 部分将更详细地讨论这些)。






为 Derby 设置 iBATIS

因为将使用 Derby 的嵌入式模式,且 iBATIS 抽象了所有数据库访问,所以只需要向正确的驱动程序和正确的数据库 URL 提供 iBATIS 即可。iBATIS 启动数据库,然后在任何对 iBATIS 框架进行特定于数据库的调用时提供对数据库的访问。

完成这一简单设置之后,就为使用 Data Mapper 框架构建一个简单的示例应用程序做好了准备。

测试 Derby 和 iBATIS

当您完成本教程系列的第 1 部分中的操作时,将在第 2 部分和第 3 部分中使用 JPetStore 应用程序。JPetStore 应用程序是一个示例 iBATIS 应用程序,可以从 iBATIS 框架中下载它。如果想获得一些额外的知识或想了解一下 iBATIS,可从 iBATIS 的 Web 站点下载它(请参阅参考资料 来获得链接)。

现在,您已经了解了 iBATIS 的一些基础知识和它的一些概念,因此可以在一个小的示例中好好运用一下。在这一节中,将创建一个简单的 Product 类和一个 Sequence 类。可以将 Sequence 类用作定义产品的惟一键的主键生成器。接下来将带领您一个示例组件接一个示例组件地进行查看,在这一小节的最后,会将这些示例组件组合在一起。

定义对象

首先,定义您想一直用于数据的 Java 对象。这里没什么特别之处,因为 iBATIS 不要求扩展任何超类或实现任何接口。清单 9 定义了 Sequence 类。


清单 9. Sequence 类
public class Sequence {
private String sequenceName;
private int nextId;

public Sequence(String sequenceName, int nextId) {
this.nextId = nextId;
this.sequenceName = sequenceName;
}

public Sequence() {
}

public int getNextId() {
return nextId;
}

public void setNextId(int nextId) {
this.nextId = nextId;
}

public String getSequenceName() {
return sequenceName;
}

public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
}

清单 10 显示了 Product 类。


清单 10. Product 类
public class Product {
private int productId;
private String productName;
private String productDesc;
private int quantity;

public Product(int productId, String productName,
String productDesc,int quantity) {
this.productDesc = productDesc;
this.productId = productId;
this.productName = productName;
this.quantity = quantity;
}

public Product() {
}

public int getProductId() {
return productId;
}

public void setProductId(int productId) {
this.productId = productId;
}

public String getProductName() {
return productName;
}

public void setProductName(String productName) {
this.productName = productName;
}
//...
}

正如您可以看到的,这两个类都只是具有 getter/setter 方法和少数成员的普通 JavaBeans。因为想要这些映射到数据库的某些表的类,所以现在要定义一些数据库表。






定义表

您需要一个表来存储产品,还需要一个从中获得产品并增加产品序列的另一个表,可将该序列用作主键。清单 11 显示了 Derby 很友好地为您维护的数据模型。


清单 11. 数据模型
CREATE TABLE SEQUENCE
(
SEQUENCE_NAME VARCHAR(32) NOT NULL,
NEXT_ID INTEGER NOT NULL,
CONSTRAINT SEQ_PK PRIMARY KEY (SEQUENCE_NAME)
);

CREATE TABLE PRODUCT (
PRODUCT_ID INTEGER,
PRODUCT_NAME VARCHAR(128),
PRODUCT_DESC VARCHAR(512),
QUANTITY INTEGER,
CONSTRAINT PROD_PK PRIMARY KEY (PRODUCT_ID)
);


INSERT INTO SEQUENCE (SEQUENCE_NAME, NEXT_ID)
VALUES ('ProductSeq', 1000);

现在有了一个 SEQUENCE 表,可从该表中选择产品并增加产品序列,生成 PRODUCT 表的主键。您已经在 SEQUENCE 表中插入了一行,并且将从产品序列 1000 开始操作。您还有一个 PRODUCT 表,该表包含描述某一产品的一些典型属性(但并没有包含所有属性)的少数列。






创建 SQL Map

现在需要创建描述您想要在 ProductSequence 对象上执行的数据库操作的 SQL Map。这些数据库操作通常包括插入、查询、更新和删除。

清单 12 显示了用于 Product 类的 SQL Map。


清单 12. 用于 Product 类的 SQL Map
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Product">

<typeAlias alias="product"
type="net.humandoing.invoicing.domain.Product"/>

<resultMap id="productResult" class="product">
<result property="productId" column="PRODUCT_ID"/>
<result property="productName" column="PRODUCT_NAME"/>
<result property="productDesc" column="PRODUCT_DESC"/>
<result property="quantity" column="QUANTITY"/>
</resultMap>

<update id="updateProduct" parameterClass="product">
update PRODUCT set PRODUCT_NAME = #productName#,
PRODUCT_DESC = #productDesc#,
QUANTITY = #quantity# where
PRODUCT_ID = #productId#
</update>

<select id="getProduct" resultMap="productResult"
parameterClass="product">
select PRODUCT_ID, PRODUCT_NAME, PRODUCT_DESC, QUANTITY
from PRODUCT where PRODUCT_ID = #productId#
</select>

<insert id="insertProduct" parameterClass="product">
insert into PRODUCT (product_id, product_name, product_desc, quantity)
values (#productId#, #productName#, #productDesc#, #quantity#)
</insert>

</sqlMap>

注意 typeAlias 元素。它只允许通过更短的别名(在这里是 product)而不是完全限定类名称来引用类。

清单 12 中还可以看到有一个 resultMap 元素,该元素描述您想要将通过执行某一查询获得的将 ResultSet 映射到 Product 对象的方式。这一特殊的 resultMap 将数据库中的 PRODUCT_ID 列映射到 Product 类的 productId 属性,并将数据库中的 PRODUCT_NAME 列映射到 Product 类的 productName 属性,依此类推。

现在来快速查看一下用于 Sequence 类的 SQL Map,如清单 13 中所示。


清单 13. 用于 Sequence 类的 SQL Map
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-2.dtd">

<sqlMap namespace="Sequence">

<typeAlias alias="sequence"
type="net.humandoing.invoicing.domain.Sequence"/>

<resultMap id="sequenceResult" class="sequence">
<result property="sequenceName" column="SEQUENCE_NAME"/>
<result property="nextId" column="NEXT_ID"/>
</resultMap>

<select id="getSequence" resultMap="sequenceResult"
parameterClass="sequence">
select SEQUENCE_NAME, NEXT_ID from SEQUENCE
where SEQUENCE_NAME = #sequenceName#
</select>

<update id="updateSequence" parameterClass="sequence">
update SEQUENCE set NEXT_ID = #nextId#
where SEQUENCE_NAME = #sequenceName#
</update>

</sqlMap>

这里有两个 Mapped Statement:一个用来获得序列,一个用来更新序列。还有一个 resultMap 元素,该元素描述如何将 SEQUENCE 表列映射到 Sequence Java 对象。






测试代码

为了让一切顺利,所有测试都作为 Ant build 文件的少数目标包含在内。因此可以通过执行这些目标来运行测试。在运行测试之前,要查看一下 sql-map-config.xml 文件,该文件将初始化 Data Mapper 框架,以便使用其 sql-map-config.xml 文件(参见清单 14)。


清单 14. sql-map-config.xml 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

<properties resource="properties/database.properties"/>

<settings cacheModelsEnabled="true" enhancementEnabled="false"
maxSessions="64" maxTransactions="8" maxRequests="128"/>

<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property value="${driver}" name="JDBC.Driver"/>
<property value="${url}" name="JDBC.ConnectionURL"/>
<property value="${username}" name="JDBC.Username"/>
<property value="${password}" name="JDBC.Password"/>
<property value="15" name="Pool.MaximumActiveConnections"/>
<property value="15" name="Pool.MaximumIdleConnections"/>
<property value="1000" name="Pool.MaximumWait"/>
</dataSource>
</transactionManager>

<sqlMap resource="net/humandoing/invoicing/domain/sql/Sequence.xml"/>
<sqlMap resource="net/humandoing/invoicing/domain/sql/Product.xml"/>

</sqlMapConfig>

该配置简单而又直接。iBATIS 文档有少数一些例外示例,该文档更详细地描述了配置元素。主要需要注意的是,您将从位于 properties/database.properties(相对于类路径的根路径)的文件中获得数据库连接信息,还要注意的是通知 Data Mapper 您将加载两个不同的 SQL Map:SequenceProduct

清单 15 显示了获得已启动的 Data Mapper 框架所需的少数几行 Java 源代码(从 Test 类中获得)。注意,大多数代码由异常处理组成。


清单 15. 获得已启动的 Data Mapper 框架所涉及的 Java 源代码
private static SqlMapClient dataMapper = null;
//...

Reader reader = null;
try {
reader = Resources.getResourceAsReader(
"net/humandoing/invoicing/domain/sql/sql-map-config.xml");
dataMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
} catch (Exception e) {
e.printStackTrace();
}

清单 15 中一些重要的位是用粗体显示的。Reader 只是一个使用 Resources 实用程序类的 java.io.Reader,它使文件更易于为您所用,该实用程序类是随 iBATIS 一起提供的,用于从类路径中获得 sql-map-config.xml。

SqlMapClient 实例用于长期存在的对象(可能用于应用程序的整个生命周期),因此可能需要一个用于此实例的某个地方的静态初始化器 (static initializer),然后允许通过静态 getInstance() 方法类型或通过将实例绑定到 JNDI 中来访问该实例。

现在来查看来自与 Data Mapper 框架进行交互的 Test 类的其他少数代码片段,然后查看一些屏幕捕获,其中包括来自 Ant 测试目标的输出。

以下是一个用于 Sequence 的查询:

Sequence sequence = new Sequence("ProductSeq", -1);
sequence = (Sequence)
dataMapper.queryForObject("getSequence", sequence);


这就是该查询,它使用序列名 ProductSeq 创建了一个 Sequence 对象。如果您记得 Mapped Statement getSequence(来自 Sequence.xml 文件),那么或许您会记得 SELECT 语句在 SEQUENCE_NAME 的基础上执行了一个查询。在这种情况下,iBATIS 框架将接管控制权,对 Derby 数据库执行以下 SQL 语句:

select SEQUENCE_NAME, NEXT_ID from SEQUENCE
where SEQUENCE_NAME = "ProductSeq"


在执行上述代码之后,您可能想保留返回的序列值,因为要用它作为 Derby 数据库中创建的新产品的主键。或许您还想通过增加 NEXT_ID 列来更新序列(参见清单 16)。


清单 16. 增加 NEXT_ID 列
int nextId = sequence.getNextId();

//Update the sequence by incrementing the NEXT_ID
//column.
sequence.setNextId(sequence.getNextId() + 1);
dataMapper.update("updateSequence", sequence);

在这里,已经将下一个有效 ID 保存到 nextId 变量中,然后增加该值并请求 Data Mapper 框架更新 Derby 中的记录。现在要创建一个产品,如清单 17 中所示。


清单 17. 创建一个产品
Product product = new Product(nextId, 
"iBATIS Sandwich",
"A Yummy Sandwich",
24);

product = (Product) dataMapper.insert("insertProduct", product);


瞧!现在有了一个也保存在该数据库中的产品。最后但非最不重要的是,执行一个查询确保该产品保存在数据库中。毕竟编程人员都是一些怀疑论者。任何易于使用的数据映射框架几乎都是看起来很好,但实际并非如此。

Product queryProd = new Product();
queryProd.setProductId(nextId);
queryProd = (Product) dataMapper.queryForObject("getProduct", queryProd);


在此查询中,创建了一个新的 Product 对象并设置 productId(将它设置为刚才插入的产品的值)。然后继续请求 Data Mapper 框架使用 getProduct 的 ID 执行 Mapped Statement,它返回一个完全填充的 Product 对象,该对象是从 Derby 数据库中查询获得的。

现在来快速查看如何可以自己运行这些测试中的一些测试。





运行测试

要做的第一件事是创建 Derby 数据库中需要的表。该示例中包含一个用于该操作的 Ant 目标。在终端窗口或 IDE 中运行 ant create-database(确保当前目录位于项目的根目录上),您将获得与图 1 中所示输出类似的东西。


图 1. 来自 create-database 目标的 Ant 输出
来自 create-database 目标的 Ant 输出

BUILD SUCCESSFUL —— 这是每个人都喜欢看到的。现在已经创建好了表,可以快速查询 PRODUCT 表,查看里面是否有东西。运行 ant get-products,它将查询 Derby 数据库的 PRODUCT 表中的所有行。您应该接收到一些类似图 2 中所示的输出。


图 2. 来自 get-products 目标的 Ant 输出
来自 get-products 目标的 Ant 输出

在这里所能看到的就是数据库中没有任何产品;可以看到一些列标题,但没有产品。现在运行一个测试,该测试获得一个 Sequence、更新它、创建一个 Product 并随后查询该产品。用于此操作的 Ant 目标是 ant run-test图 3 中显示了结果。


图 3. 来自 run-test 目标的 Ant 输出
来自 run-test 目标的 Ant 输出

您已经查询了名为 ProductSeqSequence,它有一个来自数据库的为 1000 的 NEXT_ID。然后您可以创建一个 Product图 3 中看到的输出是在已将产品插入数据库之后使用 Data Mapper 框架查询该产品所获得的结果。您可以根据自己的需要多次运行 ant run-test 目标。它会继续将 iBATIS Sandwiches 插入 Derby 数据库并继续增加序列。

最后,通过再次运行 ant get-products 目标并查询数据库来获得所有产品行,可以打消您的疑虑。可以在图 4 中查看结果。


图 4. 插入某一产品后来自 get-products 目标的输出
插入某一产品后来自 get-products 目标的输出

图 4 中可以看到,已经通过 iBATIS Data Mapper 框架将 iBATIS Sandwich 成功添加到 Derby 数据库中。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值