iBATIS笔记之数据映射工作方式
数据映射定义在一个描述文件中。通过使用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="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的形式之后,虽然变的更长更复杂,但与程序代码之间的耦合降至了最低。
在一个Data Map定义中可以依你所想添加多个cache models、别名、resultMaps、parameterMaps和statements元素,所有定义会被加载到同一个configuration中,因此你可以在一个DataMap中使用定义在另一个DataMap中的元素。
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>
中括号内的项是可选的,甚至有些项之间是互相排斥的。
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地址,当然同时也对参数对象进行同样操作。
NOTE:对于.NET而言,parameterMap是必需的,DBType, parameter direction, size, precision, scale等属性通常由框架依据你的Provider自动去发现。(基于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语句做到化繁为简,但它却做到了简者更简。