iBatis SqlMap的配置总结
核心提示:SqlMap的配置是iBatis中应用的核心。这部分任务占据了iBatis开发的70的工做量。java
一、命名空间: sqlMap namespace=Account,在此空间外要引用此空间的元素,则须要加上命名空间名。 sql
二、实体的别名: typeAlias alias=Account type=com.lavasoft.ibatissut.sim
SqlMap的配置是iBatis中应用的核心。这部分任务占据了iBatis开发的70的工做量。
一、命名空间:
数据库
<sqlMap namespace="Account">,在此空间外要引用此空间的元素,则须要加上命名空间名。
二、实体的别名:
apache
<typeAlias alias="Account" type="com.lavasoft.ibatissut.simple.domain.entity.Account"/>
若是有用到的全名的地方,能够用别名代替,受命名空间约束。
三、插入操做
对于自增主键的表,插入能够不配置插入的主键列。不然是必须的。
四、获取主键
插入语句以前配置:主要是针对Sequence主键而言,插入前必须指定一个主键值给要插入的记录。Oracle、DB2亦如此,方法是在插入语句标签<insert....>以前配置上:
缓存
<insert id="insertAccount" parameterClass="Account"> <selectKey resultClass="long" keyProperty="sctId"> SELECT SEQ_TEST.NEXTVAL FROM DUAL </selectKey$amp;>amp;$nbsp; insert into .... ........ </insert>
插入语句以后配置:主要是针对自增主键的表而言,这类表在插入时不须要主键,而是在插入过程自动获取一个自增的主键。好比MySQL
dom
<insert id="insertAccount" parameterClass="Account"> <selectKey resultClass="long" keyProperty="sctId"> SELECT LAST_INSERT_ID() </selectKey$amp;>amp;$nbsp; insert into .... ........ </insert>
固然,是否须要配置<selectKey>根据状况,只要能保证记录有主键便可。一旦配置了<selectKey>,就能够在执行插入操做时获取到新增记录的主键。
六、SQL入参parameterClass
插入语句入参:parameterClass="类别名" 来设定。
查询语句入参:能够设定类别名,也能够设定为map,也能够设定为iBatis支持的原生类型(好比string、int、long等),当只有一个原生类型入参时,使用#value#来引用,这个value是否是关键字,可变。好比:
工具
<select id="getById" parameterClass="long" resultMap="result_base"> select * from customer where id = #value# </select>
map是最强大的入参方式,任何入参方式均可以转换为这种入参方式,由于iBatis仅接受一个入参,当几个参数分布在不一样对象中的时候,将这些对象的属性(或者对象自己put)到map中,而后一次传递给sql语句是很是有效。能够本身写一个将对象或者对象集合转换为map的工具(我已经实现一个了)。
另外,map的中的元素(好比pobj)是个复杂对象,则还能够在SQL中以#pobj.protyename#的格式来引用其中内嵌的属性。固然不推荐这么干。
七、返回值参数类型
返回值参数也一样有两种类型,一种是对象类型resultClass="Account",一种是resultMap="AccountResult"。这两种类型的选择经常会使人疑惑不解,一言明其理:
当结果集列名和类属性名彻底对应的时候,则应该使用resultClass来指定查询结果类型。固然有些列明不对应,能够在sql中使用as重命名达到一致的效果。
当查询结果列名和类属性名对应不上的时候,应该选择 resultMap指定查询结果集类型。不然,则查询出来填充的对象属性为空(数字的为0,对象的为null)。
可是实际上 resultMap是对一个Java Bean的映射,须要先定义xml的映射后,才能够引用,例如:
post
<resultMap id="AccountResult" class="Account"> <result property="id" column="ACC_ID"/> <result property="firstName" column="ACC_FIRST_NAME"/> <result property="lastName" column="ACC_LAST_NAME"/> <result property="emailAddress" column="ACC_EMAIL"/> </resultMap> resultMap映射的结果的目的就是要将查询的结果集绑定到映射对象的属性上。
无论使用哪一种返回值参数类型,其最终目的就是要把每条记录映射到一个类的对象或者对象集合上,若是有某个类属性映射不上,则在获得的这个对象或对象集合中这个属性为空。映射的属性能够是表与实体中的一部分。不要同时使用两种返回值参数类型,这样只会使人迷惑。
八、查询结果集分组
查询结果集排序有两种方式:一是在结果集映射上定义<resultMap id="result" class="bar" groupBy="id">,另外一种就是在SQL语句中分组。建议在SQL语句中分组,以得到更大的可控制性。
九、 SQL中参数的引用
SQL中引用parameterClass的参数有三种方式:
iBatis内置支持的类型,好比int、string,使用#value#来引用,这个value是否是关键字,可变。
map类型的参数,使用#keyName#来引用,keyName为键名。
复杂对象的参数,使用#propertyName#来引用,propertyName类属性的名字。
十、模糊查询中参数的引用
模糊查询是针对字符串而言的,若是遇到两个单引号要包含一个参数,则不能再用#来引用变量了,而应该改成$,好比:'%$varName$%',固然,也可使用 '%' || #varname# || '%' 来绕过此问题。
十一、SQL片断
能够经过<sql id="sql_xxx">...</sql>定义SQL片断,而后<include refid="sql_xxx"/>来在各类语句中引用,达到复用目的。
十二、动态SQL
能够经过使用动态SQL来组织灵活性更大的更通用的SQL,这样极大减小了编码量,是iBatis应用的第二大亮点。
好比:一个动态的where条件
性能
<dynamic prepend="where"> <isNotEmpty prepend="and" property="$$$$$"> $name like '%'|| #$name# ||'%' </isNotEmpty> <isGreaterThan prepend="and" property="$$$$$" compareValue="$$$number"> $code like '%'|| #$code# ||'%' </isGreaterThan> </dynamic>
固然,prepend表示连接关键字,能够为任何字符串,当为sql关键字时,iBatis自动判断是否应该添加该关键字。该语法也很简单,关键是要会用心思考组织动态SQL。
这里面有一点要注意:区别<isNotEmpty>和<isNotNull>区别,当为空空串时<isNotEmpty>返回true,当为空串时<isNotNull>返回真。哈哈,本身体会吧,说了反而啰嗦。
1三、结果集映射继承
结果集映射的继承的目的是为了映射定义的复用,好比下面定义了两个映射,AccountResult继承了base:
学习
<resultMap id="base" class="Account"> <result property="id" column="ACC_ID"/> <result property="firstName" column="ACC_FIRST_NAME"/> <result property="lastName" column="ACC_LAST_NAME"/> </resultMap> <resultMap id="AccountResult" class="Account" extends="Account.base"> <result property="emailAddress" column="ACC_EMAIL"/> </resultMap>
这样,就很容易扩展了一个映射策略。
1四、查询注入
查询注入是在一个查询中嵌入另一个查询,这样作的目的是为了实现实体对象之间的关联关联关系(一对1、一对多、多对多)分单项双向。有关这些内容,是比较复杂的,笔者对此作了深刻研究,并分别写了三篇来说述。
查询注入的实现就是在实体属性为另一个实体或者实体集合的时候,引入一个相关的查询来实现,例如,客户和订单的映射关系:
public class Customer { private Long id; private String name; private String address; private String postcode; private String sex; private List<Orders> orderlist = new ArrayList<Orders>(); <resultMap id="result" class="customer"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="address" column="address"/> <result property="postcode" column="postcode"/> <result property="sex" column="sex"/> <result property="orderlist" column="id" select="orders.findByCustomerId "/> </resultMap>
在这个映射中,为了查询客户的时候,能查询到相关的订单,能够在映射orderlist 属性的时候,将其指向另一个查询orders.findByCustomerId ,这个查询是以Customer的id 为参数来查询的。
select="orders.findByCustomerId " 这个查询定义以下: <select id="findByCustomerId" resultMap="result_base" parameterClass="long"> select * from orders where customerId = #value# </select>
原理就是这么简单,而后根据实际状况,能够自由实现实体间的关联关系。
1四、iBatis的分页查询
iBatis 的分页有两种方式,一点都不神秘,不要被网上的流言所迷惑。
第一种方式:结果集筛选分页。先执行部分页的SQL查询语句,而后获得一个 ResultSet,而后根据分页范围选择有效的记录填充到对象中,最终以集合的形式返回。对于10w条一下的记录的表,不存在性能问题,若是存在,你能够选择第二中方式。
第二种方式:SQL分页,经过组装分页类型的SQL来实现分页。这个关键在于分页参数的传递和分页SQL的构建。分页SQL构件每种数据库都不同,不说了。分页参数的传递却能够通用。我主张用map分装入参,连同分页参数一块传递进来,就搞定了。若是原来没有考虑到分页,而用的是对象作参数,则能够经过apache 的 beanutils组件来实现一个object到map之间的转换工具,问题迎刃而解。
固然,这还不是分页查询应用的最高境界。思考,分页须要计算一个总记录数,记录数执行的sql返回值是count( ),条件是除了分页之外的条件,所以应该将查询SQL静态分开,以MySQL为例,能够将查询分为查什么,和什么条件两部分,在条件部分对分页参数进行动态判断,若是分页参数就不分页,若是有则分页。这样最后只须要两个组装的sql就能够计算总数和分页查询了。大大简化了问题的难度。 Oracle的解决思路也同样,不同的地方就是拼装分页SQL改变了。
1五、执行存储过程的配置
SQL Map 经过<procedure>元素支持存储过程。下面的例子说明如何使用具备输出参数
的存储过程。
<parameterMap id="swapParameters" class="map"> <parameter property="email1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/> <parameter property="email2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/> </parameterMap> <procedure id="swapEmailAddresses" parameterMap="swapParameters"> {call swap_email_address ( , )} </procedure>
调用上面的存储过程将同时互换两个字段(数据库表)和参数对象(Map)中的两个 email地址。若是参数的 mode 属性设为 INOUT 或 OUT,则参数对象的值被修改。不然保持不变。注意!要确保始终只使用 JDBC 标准的存储过程语法。参考 JDBC 的 CallableStatement文档以得到更详细的信息。1六、就是iBatis中各类id的命名了,这个看起来小菜一碟,可是搞砸了会很痛苦。建议若是有DAO层的话,DAO接口的名字和SQL语句 id的名字保持一致。同时,在DAO中将save和update封装为一个方法(从Hibernate中学来的),这是很是好的。也能够直接在SQL层将插入和更新柔和在一块,太复杂,有点影响效率,这见机行事了。 另外Spring提供了各类数据操做模板,经过模板,擦作数据也就是“一句话”的问题,写个DAO还有必要么,尤为对iBatis来讲,根本没有必要。这样,就须要在领域活动层的设计上下功夫了。17 、iBatis的查询也能够配置缓存策略,缓存的配置很复杂,分不少中状况,能够参看附件中的iBATIS-SqlMaps-2_cn.pdf 的39页内容,有详细介绍。1八、偷懒的最高境界,让程序去干哪里80%的体力活。本身仅仅把把关。任何重复的活动都有规律可循的,一旦发现了其中的规律,你就能够想办法把本身从中解脱出来。 iBatis也不例外,每一个表都有增删改查、分页等操做。对应在每一个DAO方法上亦如此。能够经过数据库生成sqlmap、entity、dao,而后将这些东西改吧改吧就完成大部分的工做量。本人已经实现过了,固然开发这个工具的前提是你对iBatis有深刻研究和理解。-------------------------------------------------下面是iBatis开发指南中内容:附录:容易出错的地方本附录是译者添加的,列出了初学者容易出错的地方,做为完成快速入门课程后的学习笔记,可让初学者少走些弯路。仅供参考。1) 在 parameterMap 和 resultMap 中,字段数据类型是 java.sql.Types 类定义的常量名称。经常使用的数据类型包括 BLOB,CHAR,CLOB,DATE,LONGVARBINARY,INTEGER,NULL,NUMERIC,TIME,TIMESTAMP 和 VARCHAR 等。2) 对于数据表中 NULLABLE 的字段,必须在 parameterMap 和 resultMap 中指定字段的数据类型。3) 对于数据类型是 DATE,CLOB 或 BLOB 的字段,最好在 parameterMap 和 resultMap中指定数据类型。4) 对于二进制类型的数据,能够将 LONGVARBINARY 映射成 byte[]。5) 对于文本类型较大的数据,能够将 CLOB 映射成 String。6) Java Bean 必须拥有缺省的构造器(即无参数的构造器)。7) Java Bean 最好实现 Serializable 接口,以备应用的进一步扩展。本人认为:尽可能避免在每一个入参后面附加参数的类型。以保持配置简洁,而且本人在长期开发中,没有发现必需要那么作