首页 > 代码库 > iBATIS开发指南笔记
iBATIS开发指南笔记
第一部分 概述
(一)目标和初衷
1. iBATIS的目标是:用少量的代码获得大量的数据访问功能
2. 初衷是让程序员将如下过程做的更好更简单:
Separating SQL code from programming code
将SQL代码从程序代码中分离
Passing input parameters to the library classes and extracting the output
对类库传递输入参数来提取输出结果
Separating data access classes from business logic classes
将数据访问从业务逻辑类中分离
Caching often-used data until it changes
将常用数据进行缓存,除非其发生变化
Managing transactions and threading
对数据交换和线程进行管理
(二)iBATIS映射工作流的描述
1.图示:
iBATIS DataMapper workflow
2.映射工作流的具体描述:
1)提供所需要的参数,可以是对象也可以是值类型。此参数用于在SQL语句或存储过程中设定一些运行时的值。也可在不必要的情况下省略此定义。
2)通过传递参数和定义在XML描述文件中的过程或声明的名称,来执行映射。框架会准备好SQL声明语句段, 或存储过程,用传入的参数设定所有运行时必需的值,执行后返回结果。
3)如果是update语句,已更改的行数会被返回,如果是一个查询语句,返回一个对象或一个对象集合。
(三)示例用法
1.XML描述文件部分:
<insert id="InsertLineItem" parameterClass="LineItem">
INSERT INTO [LinesItem]
(Order_Id, LineItem_LineNum, Item_Id, LineItem_Quantity, LineItem_UnitPrice)
VALUES
(#Order.Id#, #LineNumber#, #Item.Id#, #Quantity#, #Item.ListPrice#)
<selectKey type="post" resultClass="int" property="Id" >
select @@IDENTITY as value
</selectKey>
</insert>
2.C#语句:
Mapper.Instance().Insert("InsertLineItem",lineItem);
ps1:若数据库可自动生成主键,通过如下语句执行插入语句,则主键值可被返回
int myKey=Mapper.Instance().Insert("InsertLineItem",lineItem);
ps2:描述文件中,<selectKey>部分规定了从数据库中返回自动生成的主键。
(四)如何确定是否应该使用iBATIS
1.iBATIS的角色是在一个对象的属性和数据库查询的列(包括存储过程)之间建立映射。如果项目是基于业务对象(包括Maps或IDictionary),则iBATIS是不错的选择。如果项目是分层架构的,以便业务层于用户接口层相互独立,iBATIS能发挥的更好,
2.使用OR/M更好的情况:
Have complete control over your database implementation
可以对数据库实现完全掌控
Do not have a Database Administrator or SQL guru on the team
队伍中没有DBA或者SQL大牛
Need to model the problem domain outside the database as an object graph.
需要在数据库外部,将问题域构建成图表对象形式
使用iBATIS更好的情况:
You do not have complete control over the database implementation, or want to continue to
access a legacy database as it is being refactored.
不能对数据库实现完全掌控,或希望在一个数据库被重构阶段依然能对其访问
You have database administrators or SQL gurus on the team.
队伍中有DBA或者SQL大牛
The database is being used to model the problem domain, and the application‘s primary role is
to help the client use the database model.
数据库本身用来对问题域建模,而项目的主要角色是帮助客户使用数据库模型
第二部分 数据映射工作方式
(一)概述
数据映射定义在一个描述文件中。通过使用iBATIS提供的常规服务,XML描述文件会呈现在客户端对象中。为了访问你的数据地图,应用程序会向客户端对象发出请求并传递所需的statement名称。
(二)XML描述文件内容(也称Data Map definition file)
EX3.1 A Simple Data Map
<?xml version="1.0" encoding="UTF-8" ?>
<sqlMap namespace="LineItem"
xmlns="http://ibatis.apache.org/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<!--Type aliases allow you to use a shorter name for long fully qualified class names.-->
<alias>
<typeAlias alias="LineItem" type="NPetshop.Domain.Billing.LineItem, NPetshop.Domain" />
</alias>
<statements>
<insert id="InsertLineItem" parameterClass="LineItem">
INSERT INTO [LinesItem]
(Order_Id, LineItem_LineNum, Item_Id, LineItem_Quantity, LineItem_UnitPrice)
VALUES
(#Order.Id#, #LineNumber#, #Item.Id#, #Quantity#, #Item.ListPrice#)
</insert>
</statements>
</sqlMap>
此映射从LineItem实例中获取参数传入statements中,因此添加值得sql语句从项目代码中分离出来。而且我们可以直接对应一个库方法:
Mapper.Instance().Insert(“InsertLineItem”,lineItem);
EX3.2 A Data Map definition file with some bells and whistles
<?xml version="1.0" encoding="UTF-8" ?>
<sqlMap namespace="Product" //此命名空间和下文中的class有何关联或区别?
xmlns="http://ibatis.apache.org/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
//给type对应的类定义别名
<alias>
<typeAlias alias="Product" type="Example.Domain.Product, Example.Domain" />
</alias>
//设定缓存参数
<cacheModels>
<cacheModel id="productCache" implementation="LRU"> //???
<flushInterval hours="24"/> //维持时间上限为24小时
<property name="CacheSize" value="http://www.mamicode.com/1000" /> //保存记录数上限为1000条
</cacheModel>
</cacheModels>
//设定结果映射
<resultMaps>
//将两个结果列和对象的两个属性建立映射
<resultMap id="productResult" class="Product">
<result property="Id" column="Product_Id"/>
<result property="Description" column="Product_Description"/>
</resultMap>
</resultMaps>
//设定SQL statement
<statements>
//”?”对应着参数位置,parameterMap表示参数来源,cacheModel表示缓存参数设定
<select id="GetProduct" parameterMap="productParam" cacheModel="productCache">
select * from Products where Product_Id = ?
</select>
</statements>
//设定传入参数
<parameterMaps>
//将statement中的?与对象的Id属性建立映射
<parameterMap id="productParam" class="Product">
<parameter property="Id"/>
</parameterMap>
<parameterMaps>
</sqlMap>
许多敏捷型开发人员会先写成EX3.1的形式,之后再添加诸如缓存等等的特性。当你将映射从EX3.1改变成EX3.2的形式之后,虽然变的更长更复杂,但与程序代码之间的耦合降至了最低。
A single Data Map definition file can contain as many Cache Models, Type Aliases, Result Maps, Parameter Maps, and Mapped Statements (including stored procedures), as you like. Everything is loaded into the same configuration, so you can define elements in one Data Map and then use them in another.
EX3.3 Statement element syntax(句法)
<statement id="statement.name"
[parameterMap="parameterMap.name"]
[parameterClass="alias"]
[resultMap="resultMap.name"]
[resultClass="class.name|alias"]
[listClass="class.name|alias"]
[cacheModel="cache.name"]
[extends="statement.name"]
>
select * from Products where Product_Id = [?|#propertyName#]
order by [$simpleDynamic$]
</statement>
中括号内的项是可选的,甚至有些项之间是互相排斥的。
(三)statement详细描述
1. statement类型
statement元素可以说是一种万能元素,任何sql statement都可以在此元素标记范围内定义。这很好,但更多特定的元素类型提供了更好的检错及其他的一些功能。如第一章示例中的insert元素。下表中总结了statement不同类型的元素和他们支持的属性、特殊字段。
The six statement-type elements
Statement Element | Attributes | Child Elements | Methods |
<statement> | id parameterClass resultClass listClass parameterMap resultMap cacheModel | All dynamic elements | Insert Update Delete All query methods |
<insert> | id parameterClass parameterMap | All dynamic elements <selectKey> <generate> | Insert Update Delete |
<update> | id parameterClass parameterMap extends | All dynamic elements <generate> | Insert Update Delete |
<delete> | id parameterClass parameterMap extends | All dynamic elements <generate> | Insert Update Delete |
<select> | id parameterClass resultClass listClass parameterMap resultMap cacheModel extends | All dynamic elements <generate> | Insert Update Delete |
<procedure> | id parameterMap resultClass resultMap cacheModel | All dynamic elements | Insert Update Delete All query methods |
2.存储过程
iBATIS将存储过程视作另一种statement类型,使用方法如下所示:
EX3.4 A Data Map using a stored procedure
<!-- Microsot SQL Server -->
<procedure id="SwapEmailAddresses" parameterMap="swap-params">
ps_swap_email_address
</procedure>
...
<parameterMap id="swap-params">
<parameter property="email1" column="First_Email" />
<parameter property="email2" column="Second_Email" />
</parameterMap>
此例的功能是交换数据库的两列中存储的两个Email地址,当然同时也对参数对象进行同样操作。The parameter object is only modified if the parameter mappings mode attribute is set to InputOutput or Output. Otherwise they are left unchanged. Of course, immutable parameter objects (e.g. String) cannot be modified.//????
NOTE:对于.NET而言,parameterMap是必需的,DBType, parameter direction, size, precision, scale等属性通常由框架依据你的供应商自动去发现。(基于CommandBuilder)如果你的存储过程没有参数,则此属性为可选的。
3.SQL片段的重用
<sql id="selectItem_fragment">
FROM items
WHERE parentid = #value#
</sql>
<select id="selectItemCount" parameterClass="int" resultClass="int">
SELECT COUNT(*) AS total
<include refid="selectItem_fragment"/>
</select>
<select id="selectItems" parameterClass="int" resultClass="Item">
SELECT id, name
<include refid="selectItem_fragment"/>
</select>
<sql>标记内的是要重用的片段,<include>标记指示要重用哪个片段。在很多情况下,也可以使用statement标记中的extends属性达到同样的目标。
4.调解XML标记冲突
由于在文档中同时使用了SQL语言和XML语言,因此会出现冲突。最常见的冲突就是>和<标记的冲突。SQL语言将此用作运算符,而XML以之为标记。一个简单的解决方法是,构建CDATA元素。
<statement id=”SelectPersonsByAge” parameterClass=”int” resultClass=”Person”>
<![CDATA[
SELECT * FROM PERSON WHERE AGE>#value#
]]>
</statement>
5.自动生成键
许多数据库系统支持自动生成主键域,作为一种扩展功能。有些供应商采用预生成模式,有些采用后生成模式。不管哪一种,你都可以通过iBATIS技术中的<insert>元素中插入<selectKey>节的方式,获取一个预生成的主键。
<!—Microsoft SQL Server IDENTITY Column Example-->
<insert id=”insert Product-MS-SQL” parameterClass=”product”>
insert into PRODUCT (PRD_DESCRIPTION)
values (#description#)
<selectKey resultClass=”int” type=”post” property=”id”>
select @@IDENTITY as value
</selectKey>
</insert>
6.<generate>标记
使用<generate>标记,可以基于<parameterMap>元素中的参数,自动创建简单的SQL statement节。支持增删改查四种基本语句。
<parameterMaps>
<parameterMap id="insert-generate-params">
<parameter property="Name" column="Category_Name"/>
<parameter property="Guid" column="Category_Guid" dbType="UniqueIdentifier"/>
</parameterMap>
<parameterMap id="update-generate-params" extends="insert-generate-params">
<parameter property="Id" column="Category_Id" />
</parameterMap>
<parameterMap id="delete-generate-params">
<parameter property="Id" column="Category_Id" />
<parameter property="Name" column="Category_Name"/>
</parameterMap>
<parameterMap id="select-generate-params">
<parameter property="Id" column="Category_Id" />
<parameter property="Name" column="Category_Name"/>
<parameter property="Guid" column="Category_Guid" dbType="UniqueIdentifier"/>
</parameterMap>
</parameterMaps>
<statements>
<update id="UpdateCategoryGenerate" parameterMap="update-generate-params">
<generate table="Categories" by="Category_Id"/>
</update>
<delete id="DeleteCategoryGenerate" parameterMap="delete-generate-params">
<generate table="Categories" by="Category_Id, Category_Name"/>
</delete>
<select id="SelectByPKCategoryGenerate" resultClass="Category" parameterClass="Category"
parameterMap="select-generate-params">
<generate table="Categories" by="Category_Id"/>
</select>
<select id="SelectAllCategoryGenerate" resultClass="Category"
parameterMap="select-generate-params">
<generate table="Categories" />
</select>
<insert id="InsertCategoryGenerate" parameterMap="insert-generate-params">
<selectKey property="Id" type="post" resultClass="int">
select @@IDENTITY as value
</selectKey>
<generate table="Categories" />
</insert>
</statements>
NOTE:对应的SQL语句是在DataMapper实例化是就创建的,所以执行时不会有性能上的影响。使用<generate>标记不支持blobs等供应商的特殊类型,也不能简化复杂的SQL语句做到化繁为简,但它却做到了简者更简。