iBATIS In Action(六)执行非查询语句
5.1 更新数据的基石
在第4章中,您已经学习了所有的语句类型和基本查询相关的部分API。这里我们再来看一下执行非查询语句常用的API,然后回顾一下更新数据库相关的语句类型。
5.1.1 非查询SQL语句相关的API
我们把更新数据库的一些“高级”的技术保留在下章中,这里就仅仅看一下insert,update,delete的基本内容——更新数据库时最常用的三个方法。不过,现在只给出简单的介绍,暂时已经够用了,它们的详细内容将在本章稍后给出。
Insert方法
Insert方法接受两个参数:映射语句的名称(id)和作为参数的对象,后者包含了要插入到数据库的数据。
在更新数据库所用的三个方法中,Insert方法与另两个不同,它返回的是一个object(见5.2.3节)。
Update方法
Update方法用于执行那些对应于SQL中UPDATE语句的映射语句:
与Insert方法一样,Update方法也接受两个参数:映射语句的名称(id)和作为参数的对象。返回值则是执行UPDATE语句所影响的行数。
Delete方法
Delete方法与Update方法几乎一样,只是它是用来执行DELETE语句的:
5.1.2 非查询语句
表5.1是第4章的表4.1的子集。
语句类型 |
特性 |
子元素 |
用途 |
详细内容参考 |
<insert> |
id parameterClass parameterMap |
所有动态元素<selectKey> <generate> |
插入数据 |
5.2节; |
<update> |
id |
所有动态元素 |
更新数据 |
5.3节; |
<delete> |
id |
所有动态元素 |
删除数据 |
5.3节; |
<procedure> |
id |
所有动态元素 |
执行存储过程 |
5.5节; |
<statement> |
id parameterClass resultClass listClass parameterMap resultMap cacheModel |
所有动态元素 |
可以包含任意类型的语句,几乎无所不能 |
6.3.1节; |
译注:在iBATIS.NET的DataMapper1.6.1版本中,还有另外两个与语句相关的元素:<sql>和<include>,能给我们带来更多方便,详细内容请参看iBATIS官方文档。
5.2 插入数据
向数据库中插入数据与查询数据不尽相同,但过程却很相似。不管我们使用的是内联参数还是外部参数映射(这两种参数方式都在第4章有详细描述),在执行任何映射语句时它们的工作原理都是类似的。
5.2.1 使用内联参数
insert into account (
accountId,
username, password,
memberSince,
firstName, lastName,
address1, address2,
city, state, postalCode,
country, version)
values (
#accountId:NUMBER#,
#username:VARCHAR#, #password:VARCHAR#,
#memberSince:TIMESTAMP#,
#firstName:VARCHAR#, #lastName:VARCHAR#,
#address1:VARCHAR#, #address2:VARCHAR#,
#city:VARCHAR#, #state:VARCHAR#, #postalCode:VARCHAR#,
#country:VARCHAR#, #version:NUMBER#
)
</insert>
这是映射语句,执行它的代码(在单元测试中)则可以这么写:
account.AccountId = 9999;
account.Username = "inlineins";
account.Password = "poohbear";
account.FirstName = "Inline";
account.LastName = "Example";
SqlMapper.Insert("Account.insertWithInlineInfo", account);
这种方式确实有效,但是一旦程序中包含了<insert>和<update>语句的多个版本,代码就变得冗长而且难以维护。如果不幸发生了这种情况,那么外部参数就可以帮我们简化SQL Map文件的维护工作了。
5.2.2 使用外部参数
外部参数不仅可以提供内联参数的功能,还能改善性能并在加载时提供更多的校验(这意味着运行时的更少错误)。
这里时一个使用外部参数的<insert>语句示例,其代码在功能上与上个例子一样。<parameter property="accountId" dbType="NUMBER" />
<parameter property="username" dbType="VARCHAR" />
<parameter property="password" dbType="VARCHAR" />
<parameter property="memberSince" dbType="TIMESTAMP" />
<parameter property="firstName" dbType="VARCHAR" />
<parameter property="lastName" dbType="VARCHAR" />
<parameter property="address1" dbType="VARCHAR" />
<parameter property="address2" dbType="VARCHAR" />
<parameter property="city" dbType="VARCHAR" />
<parameter property="state" dbType="VARCHAR" />
<parameter property="postalCode" dbType="VARCHAR" />
<parameter property="country" dbType="VARCHAR" />
<parameter property="version" dbType="NUMBER" />
</parameterMap>
<insert id="insertWithExternalInfo"
parameterMap="fullParameterMapExample">
insert into account (
accountId,
username, password,
memberSince
firstName, lastName,
address1, address2,
city, state, postalCode,
country, version)
values (?,?,?,?,?,?,?,?,?,?,?,?,?)
</insert>
可是看起来并没简单多少啊!不过如果语句多了,差别就明显了。不仅更为简单(因为不再需要为每个属性指定类型),而且因为有了统一的维护点,如果要修改Parameter Map,只要修改一处即可。
例如,在传入memberSince的每个地方,iBATIS都会自动将其作为TIMESTAMP数据库类型来处理。过了一段时间,我们觉得DATETIME就够了,只要修改一处——Parameter Map。
在前面的两个例子中,执行语句的代码是一样的(除了语句的名称):sqlMap.Insert("Account.insertWithExternalInfo", account);
到这里我们可以看到,内联参数和外部参数的区别在于可维护性和性能——外部参数对此都进行了优化。
5.2.3 自动生成的主键
对于任何数据库来说,提供唯一标识数据表中一行记录的能力是至关重要的。几乎所有数据库都提供了为新添加的行自动生成主键的方法。这样再操作数据库的时候比较方便,但它也带来了一个问题,如果我们需要知道新生成的主键值该怎么办?
有的数据库供应商是预先生成(pre-generate)主键的(如Oracle和PostgreSQL),有的则是事后生成(post-generate)的(如SQL Server和MySQL)。不管是哪种方式,我们都可以使用<selectKey>节点来获取<insert>语句所产生的主键。下面的例子演示了这两种方式下的做法:<insert id="insertProduct-ORACLE" parameterClass="product">
<selectKey resultClass="int" type="pre" property="Id" >
SELECT STOCKIDSEQUENCE.NEXTVAL AS VALUE FROM DUAL
</selectKey>
insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)
</insert>
<!-- Microsoft SQL Server IDENTITY Column Example -->
<insert id="insertProduct-MS-SQL" parameterClass="product">
insert into PRODUCT (PRD_DESCRIPTION)
values (#description#)
<selectKey resultClass="int" type="post" property="id" >
select @@IDENTITY as value
</selectKey>
</insert>
<!-- MySQL Example -->
<insert id="insertProduct-MYSQL" parameterClass="product">
insert into PRODUCT (PRD_DESCRIPTION)
values (#description#)
<selectKey resultClass="int" type="post" property="id" >
select LAST_INSERT_ID() as value
</selectKey>
</insert>