其实这个东西刚来这里的时候就已经看过了,现在已经快忘光了,还是来做个小小的总结吧。。。。
老规矩,还是先看看ibatis是干什么用的吧。
一:ibatis的作用:
SQL Map能大大减少访问关系数据库的代码。SQL Map使用简单的XML配置文件将Java Bean映射成SQL语句,对比其他的数据库持久层和ORM框架,SQL Map最大的优点在于它简单易学。要使用SQL Map,只要熟悉JavaBean,XML和SQL就能充分发挥SQL语句的能力。
也就是说ibatis是用来简化我们对数据库操作的,从以前看过的东西里面现在能回忆起来的也就是把SQL跟bean挂钩之类的东东了。。。
二:具体的细节:
开始的话让我们看看这个家伙是怎么运行的!!
将一个对象作为参数,用对象参数来设定SQL语句的语句的参数。
1、执行mapped statement。用对象为PreparedStatement设置参数,执行PreparedStatement从ResultSet创建结果对象。
2、执行SQL的更新数据语句时,返回受影响的数据行数。执行查询语句时,将返回一个结果对象或对象的集合。和参数对象一样,结果对象可以是Java Bean,Map实现和基本数据类型的包装类。
sqlMapConfig配置文件的作用就不多说了,其中有很多的属性很烦人,有时候我们学一个东西的时候上来就像让他开始运行,而不是抓耳挠腮地去揣摩这些很零散的东东是干什么用的。不过在需要的时候就会发现,这些东西还是不得不知道的,最起码我们得知道他们常用的属性吧。呵呵。。
下面是一个偷过来的例子。唉,我写的东西大部分都是偷过来的。。
<?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=" examples/sqlmap/maps/SqlMapConfigExample.properties " />
<settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5"
useStatementNamespaces="false"
/>
<typeAlias alias="order" type="testdomain.Order"/>
<transactionManager type="JDBC" >
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${driver}"/>
<property name="JDBC.ConnectionURL" value="${url}"/>
<property name="JDBC.Username" value="${username}"/>
<property name="JDBC.Password" value="${password}"/>
<property name="JDBC.DefaultAutoCommit" value="true" />
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumCheckoutTime" value="120000"/>
<property name="Pool.TimeToWait" value="500"/>
<property name="Pool.PingQuery" value="select 1 from ACCOUNT"/>
<property name="Pool.PingEnabled" value="false"/>
<property name="Pool.PingConnectionsOlderThan" value="1"/>
<property name="Pool.PingConnectionsNotUsedFor" value="1"/>
</dataSource>
</transactionManager>
<sqlMap resource="examples/sqlmap/maps/Person.xml" />
</sqlMapConfig>
1、<properties>元素用来配置java的标准属性文件。如果属性文件中包括driver=org.hsqldb.jdbcDriver,那可以使用占位符${driver}来代表值org.hsqldb.jdbcDriver。属性文件中的值可以用<property name="JDBC.Driver" value="${driver}"/>来设置。
可以在不同的环境中用这个来配置不同的信息。而不是直接该XML文件中的值。
补充下,properties属性是可以用url来设置的。
2、<setting>元素用于配置和优化SqlMapClient实例的各选项。它所包含的属性如下:
maxRequests:同时执行SQL语句的最大线程数。
maxSessions:同一时间内活动的最大session数。
maxTransactions:同时进入SqlMapClient.startTransaction()的最大线程数。
cacheModelsEnabled:全局性地启用或禁用SqlMapClient的所有缓存model。调试程序时使用。
lazyLoadingEnabled:全局性地启用或禁用SqlMapClient的所有延迟加载。调试程序时使用。
enhancementEnabled:全局性地启用或禁用运行时字节码增强,以优化访问Java Bean属性的性能,同时优化延迟加载的性能。
useStatementNamespaces:如果启用本属性,必须使用全限定名来引用mapped statement。
3、<typeAlias>元素让您为一个通常较长的、全限定类名指定一个较短的别名。例:
<typeAlias alias="shortname" type="com.long.class.path.Class"/>
4、<transationManager>元素让您为SQL Map配置事务管理服务。属性type指定所使用的事务管理器类型。这个属性值可以是一个类名,也可以是一个别名。包含在框架的三个事务管理器分别是:JDBC,JTA和EXTERNAL。
5、<datasource>是<transactionManager>的一部分,为SQL Map数据源设置了一系列参数。
DataSource Factory的三个实现及其例子:
A、SimpleDataSourceFactory为DataSource提供了一个基本的实现,适用于在没有J2EE容器提供DataSource的情况。它基于iBatis的SimpleDataSource连接池实现。
<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="org.postgresql.Driver"/>
<property name="JDBC.ConnectionURL" value="jdbc:postgresql://server:5432/dbname"/>
<property name="JDBC.Username" value="user"/>
<property name="JDBC.Password" value="password"/>
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumCheckoutTime" value="120000"/>
<property name="Pool.TimeToWait" value="10000"/>
<property name="Pool.PingQuery" value="select * from dual"/>
<property name="Pool.PingEnabled" value="false"/>
<property name="Pool.PingConnectionsOlderThan" value="0"/>
<property name="Pool.PingConnectionsNotUsedFor" value="0"/>
</dataSource>
</transactionManager>
B、DbcpDataSourceFactory实现使用Jakarta DBCP(Database Connection Pool)的DataSource API提供连接池服务。适用于应用/Web容器不提供DataSource服务的情况,或执行一个单独的应用。
<transactionManager type="JDBC">
<dataSource type="DBCP">
<property name="JDBC.Driver" value="${driver}"/>
<property name="JDBC.ConnectionURL" value="${url}"/>
<property name="JDBC.Username" value="${username}"/>
<property name="JDBC.Password" value="${password}"/>
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumWait" value="60000"/>
<property name="Pool.ValidationQuery" value="select * from ACCOUNT"/>
<property name="Pool.LogAbandoned" value="false"/>
<property name="Pool.RemoveAbandoned" value="false"/>
<property name="Pool.RemoveAbandonedTimeout" value="50000"/>
</datasource>
</transactionManager>
C、JndiDataSourceFactory在应用容器内部从JNDI Context中查找DataSource实现。当使用应用服务器,并且服务器提供了容器管理的连接池和相关DataSource实现的情况下,可以使用JndiDataSourceFactory。使用JDBC DataSource的标准方法是通过JNDI来查找。
<transactionManager type="JDBC" >
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
</dataSource>
</transactionManager>
AND
<transactionManager type="JTA" >
<property name="UserTransaction" value="java:/ctx/con/UserTransaction"/>
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
</dataSource>
</transactionManager>
6、<sqlMap>元素用于包括SQL Map映射文件和其他的SQL Map配置文件。每个SqlMapClient对象使用的所有SQL Map映射文件都要在此声明。映射文件作为stream resource从类路径或URL读入。您必须在这里指定所有的SQL Map文件。
<sqlMap resource="com/ibatis/examples/sql/Customer.xml" />
<sqlMap url="file:///c:/config/Customer.xml " />
这个到了真正动手的时候了,不过会发现依然继续要管理细节的东西,细节决定成败啊。。。先看个例子。。
<sqlMap id=”Product”>
<cacheModel id=”productCache” type=”LRU”>
<flushInterval hours=”24”/>
<property name=”size” value=”1000” />
</cacheModel>
<typeAlias alias=”product” type=”com.ibatis.example.Product” />
<parameterMap id=”productParam” class=”product”>
<parameter property=”id”/>
</parameterMap>
<resultMap id=”productResult” class=”product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
</resultMap>
<select id=”getProduct” parameterMap=”productParam” resultMap=”productResult” cacheModel=”product-cache”>
select * from PRODUCT where PRD_ID = ?
</select>
</sqlMap>
下面来慢慢解释其中的东东。。。
Map Statement结构:
<statement id=”statementName”
[parameterClass=”some.class.Name”]
[resultClass=”some.class.Name”]
[parameterMap=”nameOfParameterMap”]
[resultMap=”nameOfResultMap”]
[cacheModel=”nameOfCache”]
>
select * from PRODUCT where PRD_ID = [?|#propertyName#]
order by [$simpleDynamic$]
</statement>
<statement>元素是个通用声明,可以用于任何类型的SQL语句。因为SQL语句是嵌在XML文档中的,因此有些特殊的字符不能直接使用,例如大于号和小于号(<>)。解决的办法很简单,只需将包含特殊字符的SQL语句放在XML的CDATA区里面就可以了。如:
<![CDATA[
SELECT *
FROM PERSON
WHERE AGE > #value#
]]>
自动生成主键:我们在数据库插入一条数据的时候,经常是需要返回插入这条数据的主键。可以使用<selectKey>节点来获取<insert>语句所产生的主键。
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
<selectKey resultClass="int" keyProperty="id" >
SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
</selectKey>
insert into PRODUCT (PRD_ID,PRD_DESCRIPTION)
values (#id#,#description#)
</insert>
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
insert into PRODUCT (PRD_DESCRIPTION)
values (#description#)
<selectKey resultClass="int" keyProperty="id" >
SELECT @@IDENTITY AS ID
</selectKey>
</insert>
存储过程: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>
parameterClass属性是可选的,但强烈建议使用。它的目的是限制输入参数的类型为指定的Java类,并优化框架的性能。如果您使用parameterMap,则没有必要使用parameterClass属性。
parameterMap属性的值等于一个预先定义的<parameterMap>元素的名称。
resultClass属性可以让您指定一个Java类,根据ResultSetMetaData将其自动映射到JDBC的ResultSet。只要是Java Bean的属性名称和ResultSet的列名匹配,属性自动赋值给列值。这使得查询mapped statement变得很短。如:
<statement id="getPerson" parameterClass=”int” resultClass="examples.domain.Person">
SELECT PER_ID as id,
PER_FIRST_NAME as firstName,
PER_LAST_NAME as lastName,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
PER_HEIGHT_M as heightInMeters
FROM PERSON
WHERE PER_ID = #value#
</statement>
resultMap可以控制数据如何从结果集中取出,以及哪一个属性匹配哪一个字段。不象使用resultClass的自动映射方法,resultMap属性可以允许指定字段的数据类型,NULL的替代值复杂类型映射(包括其他Java Bean,集合类型和基本类型包装类)。如:
<resultMap id=”get-product-result” class=”com.ibatis.example.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>
cacheModel的属性值等于指定的cacheModel元素的name属性值。属性cacheModel定义查询mapped statement的缓存。每一个查询mapped statement可以使用不同或相同的cacheModel。
<cacheModel id="product-cache" imlementation="LRU">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
<statement id=”getProductList” parameterClass=”int” cacheModel=”product-cache”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
xmlResultName属性:当直接把查询结果映射成XML document时,属性xmlResultName的值等于XML document根元素的名称。例如:
<select id="getPerson" parameterClass=”int” resultClass="xml" xmlResultName=”person”>
SELECT PER_ID as id,
PER_FIRST_NAME as firstName,
PER_LAST_NAME as lastName,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
PER_HEIGHT_M as heightInMeters
FROM PERSON
WHERE PER_ID = #value#
</select>
上面的查询结果将产生一个XML document,结构如下:
<person>
<id>1</id>
<firstName>Clinton</firstName>
<lastName>Begin</lastName>
<birthDate>1900-01-01</birthDate>
<weightInKilograms>89</weightInKilograms>
<heightInMeters>1.77</heightInMeters>
</person>
parameterMap负责将Java Bean的属性映射成statement的参数。如:
<parameterMap id=”parameterMapName” [class=”com.domain.Product”]>
<parameter property =”propertyName” [jdbcType=”VARCHAR”] [javaType=”string”]
[nullValue=”NUMERIC”] [null=”-9999999”]/>
<parameter …… />
<parameter …… />
</parameterMap>
<parameter>元素的属性:
property属性:是传给statement的参数对象的Java Bean属性名称。
jdbcType属性:用于显式地指定给本属性(property)赋值的数据库字段的数据类型。
javaType属性:用于显式地指定被赋值参数Java属性的类名。
nullValue属性:可以是对于property类型来说任意的合法值,用于指定NULL的替换值。
基本输入类型:假如没必要写一个Java Bean作为参数,可以直接使用基本类型的包装类(即String,Integer,Date等)作为参数。如:
<statement id=”insertProduct” parameter=”java.lang.Integer”>
select * from PRODUCT where PRD_ID = #value#
</statement>
Map类型输入参数:假如没必要写一个Java Bean作为参数,而要传入的参数又不只一个时,可以使用Map类(如HashMap,TreeMap等)作为参数对象。如:
<statement id=”insertProduct” parameterClass=”java.util.Map”>
select * from PRODUCT where PRD_CAT_ID = #catId# and PRD_CODE = #code#
</statement>
在这个中要注意的是得有相同类型的名称的参数,并且相应的类型也要相同。
Result Map:在执行查询Mapped Statement时,resultMap负责将结果集的列值映射成Java Bean的属性值。如:
<resultMap id=”resultMapName” class=”some.domain.Class” [extends=”parent-resultMap”]>(extends的作用感觉有点像类继承)
<result property=”propertyName” column=”COLUMN_NAME”(property返回对象的bean属性的名称,column是ResultSet中字段的名称)
[columnIndex=”1”]
[javaType=”int”] (指定java bean属性的类型)
[jdbcType=”NUMERIC”](指定ResultSet中用于赋值java bean属性字段的数据库数据类型)
[nullValue=”-999999”](指定数据库中null值的代替值)
[select=”someOtherStatement”](用于描述对象和对象之间的关系,并自动地装入复杂类型属性的数据)
/>
<result ……/>
</resultMap>
隐式的Result Map:通过设定mapped statement的resultClass属性来隐式地指定result map。诀窍在于,保证返回的ResultSet的字段名称(或标签或别名)和Java Bean中可写入属性的名称匹配。
<statement id=”getProduct” resultClass=”com.ibatis.example.Product”>
select PRD_ID as id, PRD_DESCRIPTION as description from PRODUCT where PRD_ID = #value#
</statement>
从上面的例子中还是比较容易看明白的,不过要注意的是数据库对大小写是不敏感的,这样的话就可能会出现些问题了。。
基本类型的Result(即String,Integer,Boolean):基本类型只能有一个属性,名字可以任意取。如:
<resultMap id=”get-product-result” class=”java.lang.String”>
<result property=”value” column=”PRD_DESCRIPTION”/>
</resultMap>
或者是用下面的形式:
<statement id=”getProductCount” resultClass=”java.lang.Integer”>
select count(1) as value from PRODUCT
</statement>
Map类型的Result:如:
<resultMap id=”get-product-result” class=”java.util.HashMap”>
<result property=”id” column=”PRD_ID”/>
<result property=”code” column=”PRD_CODE”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”suggestedPrice” column=”PRD_SUGGESTED_PRICE”/>
</resultMap>
或者(隐式的):
<statement id=”getProductCount” resultClass=”java.util.HashMap”>
select * from PRODUCT
</statement>
复杂类型的数据:执行的大致顺序已经标出,应该是对的:
(2)
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”category” column=”PRD_CAT_ID” select=”getCategory”/>
</resultMap>
(4)
<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>
<result property=”id” column=”CAT_ID”/>
<result property=”description” column=”CAT_DESCRIPTION”/>
</resultMap>
(1)
<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT where PRD_ID = #value#
</statement>
(3)
<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>
select * from CATEGORY where CAT_ID = #value#
</statement>
使用一个联合查询和嵌套的属性映射来代替两个查询statement(因为在上面的例子中每次查询都是要多一次的代价的,要做的是不管查多少次都只多出一次的效果)。如下:
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”category.id” column=”CAT_ID” />
<result property=”category.description” column=”CAT_DESCRIPTION” />
</resultMap>
<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT, CATEGORY where PRD_CAT_ID=CAT_ID and PRD_ID = #value#
</statement>
注:如很少有必要访问相关的对象(如Product对象的Category属性),则不用联合查询加载所有的Categor属性可能更快。
延迟加载 VS 联合查询
基本的原则是,如果您需要访问相关的对象,则使用联合查询。否则,使用延迟加载和字节码增强选项的子查询。
复杂类型集合的属性:
Result Map还可以装入代表复杂类型对象集合(List)的属性,用以表示在数据库中相互关系为多对多或一对多的数据。拥有集合属性的类作为“一”的一方,而在集合中的对象作为“多”的一方。用来装入对象集合的mapped statement和上面例子一样。唯一的不同是,让SQL Map架构装入复杂类型集合(List)的业务对象的属性必须是java.util.List或java.util.Collection类型。
<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>
<result property=”id” column=”CAT_ID”/>
<result property=”description” column=”CAT_DESCRIPTION”/>
<result property=”productList” column=”CAT_ID” select=” getProductsByCatId”/>
</resultMap>
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
</resultMap>
<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>
select * from CATEGORY where CAT_ID = #value#
</statement>
<statement id=”getProductsByCatId” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
上面一次执行的顺序是3->1->4->2。
组合键值或多个复杂参数属性:
在有多个属性之互相关联的时候就用下面的这种格式:
<resultMap id=”get-order-result” class=”com.ibatis.example.Order”>
<result property=”id” column=”ORD_ID”/>
<result property=”customerId” column=”ORD_CST_ID”/>
…
<result property=”payments” column=”{itemId=ORD_ID, custId=ORD_CST_ID}” select=” getOrderPayments”/>
</resultMap>
<statement id=”getOrderPayments” resultMap=”get-payment-result”>
select * from PAYMENT where PAY_ORD_ID = #itemId# and PAY_CST_ID = #custId#
</statement>
缓存Mapped Statement结果集
Cache model是在SQL Map XML文件中定义的可配置缓存模式,可以使用cacheModel元素来配置。例子如下:
<cacheModel id="product-cache" type ="LRU" readOnly=”true” serialize=”false”>
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”cache-size” value=”1000” />
</cacheModel>
指定mapped statement使用的cache model,例如:
<statement id=”getProductList” cacheModel=”product-cache”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
只读 VS 可读写:框架同时支持只读和可读写缓存。
Serializable可读写缓存:
缓存为每一个Session返回缓存对象不同的实例(复本)。因此每一个Session都可以安全修改返回的对象。不同之处在于,通常您希望从缓存中得到同一个对象,但这种情况下得到的是不同的对象。还有,每一个缓冲在Serializable缓存的对象都必须是Serializable的。这意味着不能同时使用Serializable缓存和延迟加载,因为延迟加载代理不是Serializable的。想知道如何把Serializable缓存,延迟加载和联合查询结合起来使用,最好的方法是尝试。要使用Serializable缓存,设置readOnly=“false”和serialize=“true”。缺省情况下,缓存是只读模式,不使用Serializable缓存。只读缓存不需要Serializable。
缓存类型:
Cache Model使用插件方式来支持不同的缓存算法。Cache Model实现的其他配置参数通过cacheModel的property元素来设。其中的四个实现:
MEMORY cache实现使用reference类型来管理cache的行为。垃圾收集器可以根据reference类型判断是否要回收cache中的数据。MEMORY实现适用于没有统一的对象重用模式的应用,或内存不足的应用。配置如下:
<cacheModel id="product-cache" type="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”reference-type” value=”WEAK” />
</cacheModel>
MEMORY cache实现只认识一个<property>元素。这个名为“reference-type”属性的值必须是STRONG,SOFT和WEAK三者其一。这三个值分别对应于JVM不同的内存reference类型。
LRU Cache实现用“近期最少使用”原则来确定如何从Cache中清除对象。
LRU Cache实现可以这样配置:
<cacheModel id="product-cache" type="LRU">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
FIFO Cache实现用“先进先出”原则来确定如何从Cache中清除对象。
FIFO Cache可以这样配置:
<cacheModel id="product-cache" type="FIFO">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
OSCACHE Cache实现是OSCache2.0缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。
OSCACHE实现可以这样配置:
<cacheModel id="product-cache" type="OSCACHE">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
</cacheModel>
OSCACHE实现不使用property元素,而是在类路径的根路径中使用标准的oscache.properties文件进行配置。在oscache.properties文件中,您可以配置Cache的算法(和上面讨论的算法很类似),Cache的大小,持久化方法(内存,文件等)和集群方法。
动态Mapped Statement:
使用参数值、参数本身和数据列都是动态的SQL,通常非常困难。典型的解决方法是,使用一系列if-else条件语句和一连串讨厌的字符串连接。对于这个问题,SQL Map API使用和mapped statement非常相似的结构,提供了较为优雅的方法。这里是一个简单的例子:
<select id="dynamicGetAccountList" cacheModel="account-cache" resultMap="account-result" >
select * from ACCOUNT
<isGreaterThan prepend="and" property="id" compareValue="0">
where ACC_ID = #id#
</isGreaterThan>
order by ACC_LAST_NAME
</select>
一个更复杂的例子:
<statement id="dynamicGetAccountList" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="WHERE">
<isNotNull prepend="AND" property="firstName">
(ACC_FIRST_NAME = #firstName#
<isNotNull prepend="OR" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
)
</isNotNull>
<isNotNull prepend="AND" property="emailAddress">
ACC_EMAIL like #emailAddress#
</isNotNull>
<isGreaterThan prepend="AND" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
</dynamic>
order by ACC_LAST_NAME
</statement>
二元条件元素
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 被比较的属性(必选)
compareProperty - 另一个用于和前者比较的属性(必选或选择compareValue)
compareValue - 用于比较的值(必选或选择compareProperty)
一元条件元素
一元条件元素检查属性的状态是否符合特定的条件。
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 被比较的属性(必选)
Parameter Present:这些元素检查参数对象是否存在。
Iterate:这属性遍历整个集合,并为List集合中的元素重复元素体的内容。
Iterate的属性:
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 类型为java.util.List的用于遍历的元素(必选)
open - 整个遍历内容体开始的字符串,用于定义括号(可选)
close -整个遍历内容体结束的字符串,用于定义括号(可选)
conjunction - 每次遍历内容之间的字符串,用于定义AND或OR(可选)
使用SQL Map API编程
配置SQL Map
一旦您创建了SQL Map XML定义文件和SQL Map配置文件,配置SQL Map就是一件极其简单的事情。SQL Map使用XmlSqlMapClientBuilder来创建。这个类有一个静态方法叫buildSqlMap。方法buildSqlMap简单地用一个Reader对象为参数,读入sqlMap-config.xml文件(不必是这个文件名)的内容。
String resource = “com/ibatis/example/sqlMap-config.xml”;
Reader reader = Resources.getResourceAsReader (resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMap(reader);
事务处理
缺省情况下,调用SqlMapClient对象的任意executeXxxx()方法将缺省地自动COMMIT/ROLLBACK。这意味着每次调用executeXxxx()方法都是一个独立的事务。这确实很简单,但对于需要在同一个事务中执行多个语句的情况(即只能同时成功或失败),并不适用。这正是事务处理要关心的事情。
如果您在使用Global Transaction(在SQL Map配置文件中设置),您可以使用自动提交并且可以得到在同一事务中执行的效果。但为了提高性能,最好是明确地划分事务的范围,因为这样做可以减少连接池的通讯流量和数据库连接的初始化。
SqlMapClient对象拥有让您定义事务范围的方法。使用下面SqlMapClient类的方法,可以开始、提交和/或回退事务:
public void startTransaction () throws SQLException
public void commitTransaction () throws SQLException
public void endTransaction () throws SQLException
开始一个事务,意味着您从连接池中得到一个连接,打开它并执行查询和更新SQL操作。使用事务处理的例子如下:
private Reader reader = new Resources.getResourceAsReader("com/ibatis/example/sqlMapconfig.xml");
private SqlMapClient sqlMap = XmlSqlMapBuilder.buildSqlMap(reader);
public updateItemDescription (String itemId, String newDescription) throws SQLException {
try {
sqlMap.startTransaction ();
Item item = (Item) sqlMap.queryForObject ("getItem", itemId);
item.setDescription (newDescription);
sqlMap.update ("updateItem", item);
sqlMap.commitTransaction ();
} finally {
sqlMap.endTransaction ();
}
}
注意!事务不能嵌套。在调用commit()或rollback()之前,从同一线程多次调用.startTransaction,将引起抛出例外。换句话说,对于每个SqlMap实例,每个线程最多只能打开一个事务。
注意!SqlMapClient事务处理使用Java的ThreadLocal保存事务对象。这意味着在处理事务时,每个调用startTransaction()的线程,将得到一个唯一的Connection对象。将一个Connection对象返回数据源(或关闭连接)唯一的方法是调用commitTransaction()或rollbackTransaction()方法。否则,会用光连接池中的连接并导致死锁。
自动的事务处理
虽然极力推荐使用明确划分的事务范围,在简单需求(通常使只读)的情况下,可以使用简化的语法。如果您没有使用startTransaction(),commitTransation()和rollbackTransaction()方法来明确地划分事务范围,事务将会自动执行。例如:
private Reader reader = new Resources.getResourceAsReader("com/ibatis/example/sqlMapconfig.xml");
private SqlMapClient sqlMap = XmlSqlMapBuilder.buildSqlMap(reader);
public updateItemDescription (String itemId, String newDescription) throws SQLException {
try {
Item item = (Item) sqlMap.queryForObject ("getItem", itemId);
item.setDescription (“TX1”);
//No transaction demarcated, so transaction will be automatic (implied)
sqlMap.update ("updateItem", item);
item.setDescription (newDescription);
item.setDescription (“TX2”);
//No transaction demarcated, so transaction will be automatic (implied)
sqlMap.update("updateItem", item);
} catch (SQLException e) {
throw (SQLException) e.fillInStackTrace();
}
}
注意!使用自动事务处理要非常小心。虽然看起来很有吸引力,但如果有多个数据更新操作要在同一事务中处理时,您会遇到麻烦。在上面的例子中,如果第二个“updateItem”操作失败,第一个“updateItem”操作仍然会执行,description会更新成“TX1”.
全局(分布式)事务
External/Programmatic Global 事务
要支持外部管理或手工编程管理全局事务,必须在SQL Map配置文件中设定<transactionManager>的type属性为EXTERNAL。使用外部管理的全局事务,SQL Map事务控制方法变得有的多余,因为事务的开始、提交和回退都由外部的事务管理器来控制。但是,使用SqlMapClient的startTransaction(),commitTransaction()或rollbackTransaction()来划分事务范围(相对于自动的事务处理),还是对提高性能有帮助。继续使用这些方法,可以保持编程规范的一致性。另一个好处是,在某些情况下,您可能需要改变关闭资源的顺序(不幸的是,不同的应用服务器和事务管理器具有不同的规则)。除了这些考虑,要使用全局事务,不需要改变您的SQL Map代码。
受管理的(Managed)全局事务
SQL Map框架也可以为您管理全局事务。要支持受管理的全局事务,必须在SQL Map配置文件中设定<transactionManager>的type属性为JTA,并设定“UserTransaction”属性为JNDI的全名,以使SqlMapClient实例能找到UserTransaction对象。
使用全局事务编程,代码没有多大的不同,但有几个小小的地方要注意。例如:
try {
orderSqlMap.startTransaction();
storeSqlMap.startTransaction();
orderSqlMap.insertOrder(…);
orderSqlMap.updateQuantity(…);
storeSqlMap.commitTransaction();
orderSqlMap.commitTransaction();
}
finally {
try {
storeSqlMap.endTransaction()
}
finally {
orderSqlMap.endTransaction()
}
}
警告!虽然这些看起来很简单,但记住不要滥用全局(分布式)事务,这点很重要。这样做既有性能方面的考虑,同时也是因为全局事务会让应用服务器和数据库驱动程序的设置变得更复杂。虽然看起来简单,您可能还是会遇到一些困难。
批处理
SQL Map API使用批处理很简单,可以使用两个简单的方法划分批处理的边界:
sqlMap.startBatch();
//…execute statements in between
sqlMap.executeBatch();
当调用endBatch()方法时,所有的批处理语句将通过JDBC Driver来执行。
用SqlMapClient执行SQL语句
SqlMapCient类提供了执行所有mapped statement的API。这些方法如下:
public int insert(String statementName, Object parameterObject) throws SQLException
public int update(String statementName, Object parameterObject) throws SQLException
public int delete(String statementName, Object parameterObject) throws SQLException
public Object queryForObject(String statementName, Object parameterObject) throws SQLException
public Object queryForObject(String statementName, Object parameterObject, Object resultObject) throws SQLException
public List queryForList(String statementName, Object parameterObject) throws SQLException
public List queryForList(String statementName, Object parameterObject, int skipResults, int maxResults) throws SQLException
public List queryForList (String statementName, Object parameterObject, RowHandler rowHandler) throws SQLException
public PaginatedList queryForPaginatedList(String statementName, Object parameterObject, int pageSize) throws SQLException
public Map queryForMap (String statementName, Object parameterObject, String keyProperty) throws SQLException
public Map queryForMap (String statementName, Object parameterObject, String keyProperty, String valueProperty) throws SQLException
代码例子
例子1:执行update(insert,update,delete)
sqlMap.startTransaction();
Product product = new Product();
product.setId (1);
product.setDescription (“Shih Tzu”);
int rows = sqlMap.insert (“insertProduct”, product);
sqlMap.commitTransaction();
例子2:查询成对象(select)
sqlMap.startTransaction();
Integer key = new Integer (1);
Product product = (Product)sqlMap.queryForObject (“getProduct”, key);
sqlMap.commitTransaction();
例子3:用预赋值的结果对象查询成对象(select)
sqlMap.startTransaction();
Customer customer = new Customer();
sqlMap.queryForObject(“getCust”, parameterObject, customer);
sqlMap.queryForObject(“getAddr”, parameterObject, customer);
sqlMap.commitTransaction();
例子4:查询成对象List(select)
sqlMap.startTransaction();
List list = sqlMap.queryForList (“getProductList”, null);
sqlMap.commitTransaction();
例子5:自动提交
//当没调用startTransaction的情况下,statements会自动提交。
//没必要commit/rollback。
int rows = sqlMap.insert (“insertProduct”, product);
例子6:用结果集边界查询成对象List(select)
sqlMap.startTransaction();
List list = sqlMap.queryForList (“getProductList”, null, 0, 40);
sqlMap.commitTransaction();
例子7:用RowHandler执行查询(select)
public class MyRowHandler implements RowHandler {
public void handleRow (Object object, List list) throws SQLException {
Product product = (Product) object;
product.setQuantity (10000);
sqlMap.update (“updateProduct”, product);
// Optionally you could add the result object to the list.
// The list is returned from the queryForList() method.
}
}
sqlMap.startTransaction();
RowHandler rowHandler = new MyRowHandler();
List list = sqlMap.queryForList (“getProductList”, null, rowHandler);
sqlMap.commitTransaction();
例子8:查询成Paginated List(select)
PaginatedList list =sqlMap.queryForPaginatedList (“getProductList”, null, 10);
list.nextPage();
list.previousPage();
例子9:查询成Map(select)
sqlMap.startTransaction();
Map map = sqlMap.queryForMap (“getProductList”, null, “productCode”);
sqlMap.commitTransaction();
Product p = (Product) map.get(“EST-93”);
三、在运用中的总结:
上面说的都是细节的东西,很多的人在学习的时候都是可以看到的,放在这里是为了以后方便查下ctrl+F嘛。不过,通过这些细节的学习会发现,在刚开始是在讲各个属性是干嘛用的,在稍微后面的时候就开始纠结怎么用才能使得效率更高一些。不可否认的是,属性的熟悉对我们很重要,但是后面的思想甚至是对数据库的了解才是最重要的,ibaits只不过是对数据库的操作的一个框架而已,我们应该话更多的时间在灵魂上!!
感觉ibatis上手还是比较容易的!
老规矩,还是先看看ibatis是干什么用的吧。
一:ibatis的作用:
SQL Map能大大减少访问关系数据库的代码。SQL Map使用简单的XML配置文件将Java Bean映射成SQL语句,对比其他的数据库持久层和ORM框架,SQL Map最大的优点在于它简单易学。要使用SQL Map,只要熟悉JavaBean,XML和SQL就能充分发挥SQL语句的能力。
也就是说ibatis是用来简化我们对数据库操作的,从以前看过的东西里面现在能回忆起来的也就是把SQL跟bean挂钩之类的东东了。。。
二:具体的细节:
开始的话让我们看看这个家伙是怎么运行的!!
将一个对象作为参数,用对象参数来设定SQL语句的语句的参数。
1、执行mapped statement。用对象为PreparedStatement设置参数,执行PreparedStatement从ResultSet创建结果对象。
2、执行SQL的更新数据语句时,返回受影响的数据行数。执行查询语句时,将返回一个结果对象或对象的集合。和参数对象一样,结果对象可以是Java Bean,Map实现和基本数据类型的包装类。
sqlMapConfig配置文件的作用就不多说了,其中有很多的属性很烦人,有时候我们学一个东西的时候上来就像让他开始运行,而不是抓耳挠腮地去揣摩这些很零散的东东是干什么用的。不过在需要的时候就会发现,这些东西还是不得不知道的,最起码我们得知道他们常用的属性吧。呵呵。。
下面是一个偷过来的例子。唉,我写的东西大部分都是偷过来的。。
<?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=" examples/sqlmap/maps/SqlMapConfigExample.properties " />
<settings
cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5"
useStatementNamespaces="false"
/>
<typeAlias alias="order" type="testdomain.Order"/>
<transactionManager type="JDBC" >
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${driver}"/>
<property name="JDBC.ConnectionURL" value="${url}"/>
<property name="JDBC.Username" value="${username}"/>
<property name="JDBC.Password" value="${password}"/>
<property name="JDBC.DefaultAutoCommit" value="true" />
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumCheckoutTime" value="120000"/>
<property name="Pool.TimeToWait" value="500"/>
<property name="Pool.PingQuery" value="select 1 from ACCOUNT"/>
<property name="Pool.PingEnabled" value="false"/>
<property name="Pool.PingConnectionsOlderThan" value="1"/>
<property name="Pool.PingConnectionsNotUsedFor" value="1"/>
</dataSource>
</transactionManager>
<sqlMap resource="examples/sqlmap/maps/Person.xml" />
</sqlMapConfig>
1、<properties>元素用来配置java的标准属性文件。如果属性文件中包括driver=org.hsqldb.jdbcDriver,那可以使用占位符${driver}来代表值org.hsqldb.jdbcDriver。属性文件中的值可以用<property name="JDBC.Driver" value="${driver}"/>来设置。
可以在不同的环境中用这个来配置不同的信息。而不是直接该XML文件中的值。
补充下,properties属性是可以用url来设置的。
2、<setting>元素用于配置和优化SqlMapClient实例的各选项。它所包含的属性如下:
maxRequests:同时执行SQL语句的最大线程数。
maxSessions:同一时间内活动的最大session数。
maxTransactions:同时进入SqlMapClient.startTransaction()的最大线程数。
cacheModelsEnabled:全局性地启用或禁用SqlMapClient的所有缓存model。调试程序时使用。
lazyLoadingEnabled:全局性地启用或禁用SqlMapClient的所有延迟加载。调试程序时使用。
enhancementEnabled:全局性地启用或禁用运行时字节码增强,以优化访问Java Bean属性的性能,同时优化延迟加载的性能。
useStatementNamespaces:如果启用本属性,必须使用全限定名来引用mapped statement。
3、<typeAlias>元素让您为一个通常较长的、全限定类名指定一个较短的别名。例:
<typeAlias alias="shortname" type="com.long.class.path.Class"/>
4、<transationManager>元素让您为SQL Map配置事务管理服务。属性type指定所使用的事务管理器类型。这个属性值可以是一个类名,也可以是一个别名。包含在框架的三个事务管理器分别是:JDBC,JTA和EXTERNAL。
5、<datasource>是<transactionManager>的一部分,为SQL Map数据源设置了一系列参数。
DataSource Factory的三个实现及其例子:
A、SimpleDataSourceFactory为DataSource提供了一个基本的实现,适用于在没有J2EE容器提供DataSource的情况。它基于iBatis的SimpleDataSource连接池实现。
<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="org.postgresql.Driver"/>
<property name="JDBC.ConnectionURL" value="jdbc:postgresql://server:5432/dbname"/>
<property name="JDBC.Username" value="user"/>
<property name="JDBC.Password" value="password"/>
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumCheckoutTime" value="120000"/>
<property name="Pool.TimeToWait" value="10000"/>
<property name="Pool.PingQuery" value="select * from dual"/>
<property name="Pool.PingEnabled" value="false"/>
<property name="Pool.PingConnectionsOlderThan" value="0"/>
<property name="Pool.PingConnectionsNotUsedFor" value="0"/>
</dataSource>
</transactionManager>
B、DbcpDataSourceFactory实现使用Jakarta DBCP(Database Connection Pool)的DataSource API提供连接池服务。适用于应用/Web容器不提供DataSource服务的情况,或执行一个单独的应用。
<transactionManager type="JDBC">
<dataSource type="DBCP">
<property name="JDBC.Driver" value="${driver}"/>
<property name="JDBC.ConnectionURL" value="${url}"/>
<property name="JDBC.Username" value="${username}"/>
<property name="JDBC.Password" value="${password}"/>
<property name="Pool.MaximumActiveConnections" value="10"/>
<property name="Pool.MaximumIdleConnections" value="5"/>
<property name="Pool.MaximumWait" value="60000"/>
<property name="Pool.ValidationQuery" value="select * from ACCOUNT"/>
<property name="Pool.LogAbandoned" value="false"/>
<property name="Pool.RemoveAbandoned" value="false"/>
<property name="Pool.RemoveAbandonedTimeout" value="50000"/>
</datasource>
</transactionManager>
C、JndiDataSourceFactory在应用容器内部从JNDI Context中查找DataSource实现。当使用应用服务器,并且服务器提供了容器管理的连接池和相关DataSource实现的情况下,可以使用JndiDataSourceFactory。使用JDBC DataSource的标准方法是通过JNDI来查找。
<transactionManager type="JDBC" >
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
</dataSource>
</transactionManager>
AND
<transactionManager type="JTA" >
<property name="UserTransaction" value="java:/ctx/con/UserTransaction"/>
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
</dataSource>
</transactionManager>
6、<sqlMap>元素用于包括SQL Map映射文件和其他的SQL Map配置文件。每个SqlMapClient对象使用的所有SQL Map映射文件都要在此声明。映射文件作为stream resource从类路径或URL读入。您必须在这里指定所有的SQL Map文件。
<sqlMap resource="com/ibatis/examples/sql/Customer.xml" />
<sqlMap url="file:///c:/config/Customer.xml " />
这个到了真正动手的时候了,不过会发现依然继续要管理细节的东西,细节决定成败啊。。。先看个例子。。
<sqlMap id=”Product”>
<cacheModel id=”productCache” type=”LRU”>
<flushInterval hours=”24”/>
<property name=”size” value=”1000” />
</cacheModel>
<typeAlias alias=”product” type=”com.ibatis.example.Product” />
<parameterMap id=”productParam” class=”product”>
<parameter property=”id”/>
</parameterMap>
<resultMap id=”productResult” class=”product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
</resultMap>
<select id=”getProduct” parameterMap=”productParam” resultMap=”productResult” cacheModel=”product-cache”>
select * from PRODUCT where PRD_ID = ?
</select>
</sqlMap>
下面来慢慢解释其中的东东。。。
Map Statement结构:
<statement id=”statementName”
[parameterClass=”some.class.Name”]
[resultClass=”some.class.Name”]
[parameterMap=”nameOfParameterMap”]
[resultMap=”nameOfResultMap”]
[cacheModel=”nameOfCache”]
>
select * from PRODUCT where PRD_ID = [?|#propertyName#]
order by [$simpleDynamic$]
</statement>
<statement>元素是个通用声明,可以用于任何类型的SQL语句。因为SQL语句是嵌在XML文档中的,因此有些特殊的字符不能直接使用,例如大于号和小于号(<>)。解决的办法很简单,只需将包含特殊字符的SQL语句放在XML的CDATA区里面就可以了。如:
<![CDATA[
SELECT *
FROM PERSON
WHERE AGE > #value#
]]>
自动生成主键:我们在数据库插入一条数据的时候,经常是需要返回插入这条数据的主键。可以使用<selectKey>节点来获取<insert>语句所产生的主键。
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
<selectKey resultClass="int" keyProperty="id" >
SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
</selectKey>
insert into PRODUCT (PRD_ID,PRD_DESCRIPTION)
values (#id#,#description#)
</insert>
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
insert into PRODUCT (PRD_DESCRIPTION)
values (#description#)
<selectKey resultClass="int" keyProperty="id" >
SELECT @@IDENTITY AS ID
</selectKey>
</insert>
存储过程: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>
parameterClass属性是可选的,但强烈建议使用。它的目的是限制输入参数的类型为指定的Java类,并优化框架的性能。如果您使用parameterMap,则没有必要使用parameterClass属性。
parameterMap属性的值等于一个预先定义的<parameterMap>元素的名称。
resultClass属性可以让您指定一个Java类,根据ResultSetMetaData将其自动映射到JDBC的ResultSet。只要是Java Bean的属性名称和ResultSet的列名匹配,属性自动赋值给列值。这使得查询mapped statement变得很短。如:
<statement id="getPerson" parameterClass=”int” resultClass="examples.domain.Person">
SELECT PER_ID as id,
PER_FIRST_NAME as firstName,
PER_LAST_NAME as lastName,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
PER_HEIGHT_M as heightInMeters
FROM PERSON
WHERE PER_ID = #value#
</statement>
resultMap可以控制数据如何从结果集中取出,以及哪一个属性匹配哪一个字段。不象使用resultClass的自动映射方法,resultMap属性可以允许指定字段的数据类型,NULL的替代值复杂类型映射(包括其他Java Bean,集合类型和基本类型包装类)。如:
<resultMap id=”get-product-result” class=”com.ibatis.example.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>
cacheModel的属性值等于指定的cacheModel元素的name属性值。属性cacheModel定义查询mapped statement的缓存。每一个查询mapped statement可以使用不同或相同的cacheModel。
<cacheModel id="product-cache" imlementation="LRU">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
<statement id=”getProductList” parameterClass=”int” cacheModel=”product-cache”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
xmlResultName属性:当直接把查询结果映射成XML document时,属性xmlResultName的值等于XML document根元素的名称。例如:
<select id="getPerson" parameterClass=”int” resultClass="xml" xmlResultName=”person”>
SELECT PER_ID as id,
PER_FIRST_NAME as firstName,
PER_LAST_NAME as lastName,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
PER_HEIGHT_M as heightInMeters
FROM PERSON
WHERE PER_ID = #value#
</select>
上面的查询结果将产生一个XML document,结构如下:
<person>
<id>1</id>
<firstName>Clinton</firstName>
<lastName>Begin</lastName>
<birthDate>1900-01-01</birthDate>
<weightInKilograms>89</weightInKilograms>
<heightInMeters>1.77</heightInMeters>
</person>
parameterMap负责将Java Bean的属性映射成statement的参数。如:
<parameterMap id=”parameterMapName” [class=”com.domain.Product”]>
<parameter property =”propertyName” [jdbcType=”VARCHAR”] [javaType=”string”]
[nullValue=”NUMERIC”] [null=”-9999999”]/>
<parameter …… />
<parameter …… />
</parameterMap>
<parameter>元素的属性:
property属性:是传给statement的参数对象的Java Bean属性名称。
jdbcType属性:用于显式地指定给本属性(property)赋值的数据库字段的数据类型。
javaType属性:用于显式地指定被赋值参数Java属性的类名。
nullValue属性:可以是对于property类型来说任意的合法值,用于指定NULL的替换值。
基本输入类型:假如没必要写一个Java Bean作为参数,可以直接使用基本类型的包装类(即String,Integer,Date等)作为参数。如:
<statement id=”insertProduct” parameter=”java.lang.Integer”>
select * from PRODUCT where PRD_ID = #value#
</statement>
Map类型输入参数:假如没必要写一个Java Bean作为参数,而要传入的参数又不只一个时,可以使用Map类(如HashMap,TreeMap等)作为参数对象。如:
<statement id=”insertProduct” parameterClass=”java.util.Map”>
select * from PRODUCT where PRD_CAT_ID = #catId# and PRD_CODE = #code#
</statement>
在这个中要注意的是得有相同类型的名称的参数,并且相应的类型也要相同。
Result Map:在执行查询Mapped Statement时,resultMap负责将结果集的列值映射成Java Bean的属性值。如:
<resultMap id=”resultMapName” class=”some.domain.Class” [extends=”parent-resultMap”]>(extends的作用感觉有点像类继承)
<result property=”propertyName” column=”COLUMN_NAME”(property返回对象的bean属性的名称,column是ResultSet中字段的名称)
[columnIndex=”1”]
[javaType=”int”] (指定java bean属性的类型)
[jdbcType=”NUMERIC”](指定ResultSet中用于赋值java bean属性字段的数据库数据类型)
[nullValue=”-999999”](指定数据库中null值的代替值)
[select=”someOtherStatement”](用于描述对象和对象之间的关系,并自动地装入复杂类型属性的数据)
/>
<result ……/>
</resultMap>
隐式的Result Map:通过设定mapped statement的resultClass属性来隐式地指定result map。诀窍在于,保证返回的ResultSet的字段名称(或标签或别名)和Java Bean中可写入属性的名称匹配。
<statement id=”getProduct” resultClass=”com.ibatis.example.Product”>
select PRD_ID as id, PRD_DESCRIPTION as description from PRODUCT where PRD_ID = #value#
</statement>
从上面的例子中还是比较容易看明白的,不过要注意的是数据库对大小写是不敏感的,这样的话就可能会出现些问题了。。
基本类型的Result(即String,Integer,Boolean):基本类型只能有一个属性,名字可以任意取。如:
<resultMap id=”get-product-result” class=”java.lang.String”>
<result property=”value” column=”PRD_DESCRIPTION”/>
</resultMap>
或者是用下面的形式:
<statement id=”getProductCount” resultClass=”java.lang.Integer”>
select count(1) as value from PRODUCT
</statement>
Map类型的Result:如:
<resultMap id=”get-product-result” class=”java.util.HashMap”>
<result property=”id” column=”PRD_ID”/>
<result property=”code” column=”PRD_CODE”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”suggestedPrice” column=”PRD_SUGGESTED_PRICE”/>
</resultMap>
或者(隐式的):
<statement id=”getProductCount” resultClass=”java.util.HashMap”>
select * from PRODUCT
</statement>
复杂类型的数据:执行的大致顺序已经标出,应该是对的:
(2)
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”category” column=”PRD_CAT_ID” select=”getCategory”/>
</resultMap>
(4)
<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>
<result property=”id” column=”CAT_ID”/>
<result property=”description” column=”CAT_DESCRIPTION”/>
</resultMap>
(1)
<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT where PRD_ID = #value#
</statement>
(3)
<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>
select * from CATEGORY where CAT_ID = #value#
</statement>
使用一个联合查询和嵌套的属性映射来代替两个查询statement(因为在上面的例子中每次查询都是要多一次的代价的,要做的是不管查多少次都只多出一次的效果)。如下:
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
<result property=”category.id” column=”CAT_ID” />
<result property=”category.description” column=”CAT_DESCRIPTION” />
</resultMap>
<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT, CATEGORY where PRD_CAT_ID=CAT_ID and PRD_ID = #value#
</statement>
注:如很少有必要访问相关的对象(如Product对象的Category属性),则不用联合查询加载所有的Categor属性可能更快。
延迟加载 VS 联合查询
基本的原则是,如果您需要访问相关的对象,则使用联合查询。否则,使用延迟加载和字节码增强选项的子查询。
复杂类型集合的属性:
Result Map还可以装入代表复杂类型对象集合(List)的属性,用以表示在数据库中相互关系为多对多或一对多的数据。拥有集合属性的类作为“一”的一方,而在集合中的对象作为“多”的一方。用来装入对象集合的mapped statement和上面例子一样。唯一的不同是,让SQL Map架构装入复杂类型集合(List)的业务对象的属性必须是java.util.List或java.util.Collection类型。
<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>
<result property=”id” column=”CAT_ID”/>
<result property=”description” column=”CAT_DESCRIPTION”/>
<result property=”productList” column=”CAT_ID” select=” getProductsByCatId”/>
</resultMap>
<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>
<result property=”id” column=”PRD_ID”/>
<result property=”description” column=”PRD_DESCRIPTION”/>
</resultMap>
<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>
select * from CATEGORY where CAT_ID = #value#
</statement>
<statement id=”getProductsByCatId” parameterClass=”int” resultMap=”get-product-result”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
上面一次执行的顺序是3->1->4->2。
组合键值或多个复杂参数属性:
在有多个属性之互相关联的时候就用下面的这种格式:
<resultMap id=”get-order-result” class=”com.ibatis.example.Order”>
<result property=”id” column=”ORD_ID”/>
<result property=”customerId” column=”ORD_CST_ID”/>
…
<result property=”payments” column=”{itemId=ORD_ID, custId=ORD_CST_ID}” select=” getOrderPayments”/>
</resultMap>
<statement id=”getOrderPayments” resultMap=”get-payment-result”>
select * from PAYMENT where PAY_ORD_ID = #itemId# and PAY_CST_ID = #custId#
</statement>
缓存Mapped Statement结果集
Cache model是在SQL Map XML文件中定义的可配置缓存模式,可以使用cacheModel元素来配置。例子如下:
<cacheModel id="product-cache" type ="LRU" readOnly=”true” serialize=”false”>
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”cache-size” value=”1000” />
</cacheModel>
指定mapped statement使用的cache model,例如:
<statement id=”getProductList” cacheModel=”product-cache”>
select * from PRODUCT where PRD_CAT_ID = #value#
</statement>
只读 VS 可读写:框架同时支持只读和可读写缓存。
Serializable可读写缓存:
缓存为每一个Session返回缓存对象不同的实例(复本)。因此每一个Session都可以安全修改返回的对象。不同之处在于,通常您希望从缓存中得到同一个对象,但这种情况下得到的是不同的对象。还有,每一个缓冲在Serializable缓存的对象都必须是Serializable的。这意味着不能同时使用Serializable缓存和延迟加载,因为延迟加载代理不是Serializable的。想知道如何把Serializable缓存,延迟加载和联合查询结合起来使用,最好的方法是尝试。要使用Serializable缓存,设置readOnly=“false”和serialize=“true”。缺省情况下,缓存是只读模式,不使用Serializable缓存。只读缓存不需要Serializable。
缓存类型:
Cache Model使用插件方式来支持不同的缓存算法。Cache Model实现的其他配置参数通过cacheModel的property元素来设。其中的四个实现:
MEMORY cache实现使用reference类型来管理cache的行为。垃圾收集器可以根据reference类型判断是否要回收cache中的数据。MEMORY实现适用于没有统一的对象重用模式的应用,或内存不足的应用。配置如下:
<cacheModel id="product-cache" type="MEMORY">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”reference-type” value=”WEAK” />
</cacheModel>
MEMORY cache实现只认识一个<property>元素。这个名为“reference-type”属性的值必须是STRONG,SOFT和WEAK三者其一。这三个值分别对应于JVM不同的内存reference类型。
LRU Cache实现用“近期最少使用”原则来确定如何从Cache中清除对象。
LRU Cache实现可以这样配置:
<cacheModel id="product-cache" type="LRU">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
FIFO Cache实现用“先进先出”原则来确定如何从Cache中清除对象。
FIFO Cache可以这样配置:
<cacheModel id="product-cache" type="FIFO">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
<property name=”size” value=”1000” />
</cacheModel>
OSCACHE Cache实现是OSCache2.0缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。
OSCACHE实现可以这样配置:
<cacheModel id="product-cache" type="OSCACHE">
<flushInterval hours="24"/>
<flushOnExecute statement="insertProduct"/>
<flushOnExecute statement="updateProduct"/>
<flushOnExecute statement="deleteProduct"/>
</cacheModel>
OSCACHE实现不使用property元素,而是在类路径的根路径中使用标准的oscache.properties文件进行配置。在oscache.properties文件中,您可以配置Cache的算法(和上面讨论的算法很类似),Cache的大小,持久化方法(内存,文件等)和集群方法。
动态Mapped Statement:
使用参数值、参数本身和数据列都是动态的SQL,通常非常困难。典型的解决方法是,使用一系列if-else条件语句和一连串讨厌的字符串连接。对于这个问题,SQL Map API使用和mapped statement非常相似的结构,提供了较为优雅的方法。这里是一个简单的例子:
<select id="dynamicGetAccountList" cacheModel="account-cache" resultMap="account-result" >
select * from ACCOUNT
<isGreaterThan prepend="and" property="id" compareValue="0">
where ACC_ID = #id#
</isGreaterThan>
order by ACC_LAST_NAME
</select>
一个更复杂的例子:
<statement id="dynamicGetAccountList" resultMap="account-result" >
select * from ACCOUNT
<dynamic prepend="WHERE">
<isNotNull prepend="AND" property="firstName">
(ACC_FIRST_NAME = #firstName#
<isNotNull prepend="OR" property="lastName">
ACC_LAST_NAME = #lastName#
</isNotNull>
)
</isNotNull>
<isNotNull prepend="AND" property="emailAddress">
ACC_EMAIL like #emailAddress#
</isNotNull>
<isGreaterThan prepend="AND" property="id" compareValue="0">
ACC_ID = #id#
</isGreaterThan>
</dynamic>
order by ACC_LAST_NAME
</statement>
二元条件元素
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 被比较的属性(必选)
compareProperty - 另一个用于和前者比较的属性(必选或选择compareValue)
compareValue - 用于比较的值(必选或选择compareProperty)
一元条件元素
一元条件元素检查属性的状态是否符合特定的条件。
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 被比较的属性(必选)
Parameter Present:这些元素检查参数对象是否存在。
Iterate:这属性遍历整个集合,并为List集合中的元素重复元素体的内容。
Iterate的属性:
prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)
property - 类型为java.util.List的用于遍历的元素(必选)
open - 整个遍历内容体开始的字符串,用于定义括号(可选)
close -整个遍历内容体结束的字符串,用于定义括号(可选)
conjunction - 每次遍历内容之间的字符串,用于定义AND或OR(可选)
使用SQL Map API编程
配置SQL Map
一旦您创建了SQL Map XML定义文件和SQL Map配置文件,配置SQL Map就是一件极其简单的事情。SQL Map使用XmlSqlMapClientBuilder来创建。这个类有一个静态方法叫buildSqlMap。方法buildSqlMap简单地用一个Reader对象为参数,读入sqlMap-config.xml文件(不必是这个文件名)的内容。
String resource = “com/ibatis/example/sqlMap-config.xml”;
Reader reader = Resources.getResourceAsReader (resource);
SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMap(reader);
事务处理
缺省情况下,调用SqlMapClient对象的任意executeXxxx()方法将缺省地自动COMMIT/ROLLBACK。这意味着每次调用executeXxxx()方法都是一个独立的事务。这确实很简单,但对于需要在同一个事务中执行多个语句的情况(即只能同时成功或失败),并不适用。这正是事务处理要关心的事情。
如果您在使用Global Transaction(在SQL Map配置文件中设置),您可以使用自动提交并且可以得到在同一事务中执行的效果。但为了提高性能,最好是明确地划分事务的范围,因为这样做可以减少连接池的通讯流量和数据库连接的初始化。
SqlMapClient对象拥有让您定义事务范围的方法。使用下面SqlMapClient类的方法,可以开始、提交和/或回退事务:
public void startTransaction () throws SQLException
public void commitTransaction () throws SQLException
public void endTransaction () throws SQLException
开始一个事务,意味着您从连接池中得到一个连接,打开它并执行查询和更新SQL操作。使用事务处理的例子如下:
private Reader reader = new Resources.getResourceAsReader("com/ibatis/example/sqlMapconfig.xml");
private SqlMapClient sqlMap = XmlSqlMapBuilder.buildSqlMap(reader);
public updateItemDescription (String itemId, String newDescription) throws SQLException {
try {
sqlMap.startTransaction ();
Item item = (Item) sqlMap.queryForObject ("getItem", itemId);
item.setDescription (newDescription);
sqlMap.update ("updateItem", item);
sqlMap.commitTransaction ();
} finally {
sqlMap.endTransaction ();
}
}
注意!事务不能嵌套。在调用commit()或rollback()之前,从同一线程多次调用.startTransaction,将引起抛出例外。换句话说,对于每个SqlMap实例,每个线程最多只能打开一个事务。
注意!SqlMapClient事务处理使用Java的ThreadLocal保存事务对象。这意味着在处理事务时,每个调用startTransaction()的线程,将得到一个唯一的Connection对象。将一个Connection对象返回数据源(或关闭连接)唯一的方法是调用commitTransaction()或rollbackTransaction()方法。否则,会用光连接池中的连接并导致死锁。
自动的事务处理
虽然极力推荐使用明确划分的事务范围,在简单需求(通常使只读)的情况下,可以使用简化的语法。如果您没有使用startTransaction(),commitTransation()和rollbackTransaction()方法来明确地划分事务范围,事务将会自动执行。例如:
private Reader reader = new Resources.getResourceAsReader("com/ibatis/example/sqlMapconfig.xml");
private SqlMapClient sqlMap = XmlSqlMapBuilder.buildSqlMap(reader);
public updateItemDescription (String itemId, String newDescription) throws SQLException {
try {
Item item = (Item) sqlMap.queryForObject ("getItem", itemId);
item.setDescription (“TX1”);
//No transaction demarcated, so transaction will be automatic (implied)
sqlMap.update ("updateItem", item);
item.setDescription (newDescription);
item.setDescription (“TX2”);
//No transaction demarcated, so transaction will be automatic (implied)
sqlMap.update("updateItem", item);
} catch (SQLException e) {
throw (SQLException) e.fillInStackTrace();
}
}
注意!使用自动事务处理要非常小心。虽然看起来很有吸引力,但如果有多个数据更新操作要在同一事务中处理时,您会遇到麻烦。在上面的例子中,如果第二个“updateItem”操作失败,第一个“updateItem”操作仍然会执行,description会更新成“TX1”.
全局(分布式)事务
External/Programmatic Global 事务
要支持外部管理或手工编程管理全局事务,必须在SQL Map配置文件中设定<transactionManager>的type属性为EXTERNAL。使用外部管理的全局事务,SQL Map事务控制方法变得有的多余,因为事务的开始、提交和回退都由外部的事务管理器来控制。但是,使用SqlMapClient的startTransaction(),commitTransaction()或rollbackTransaction()来划分事务范围(相对于自动的事务处理),还是对提高性能有帮助。继续使用这些方法,可以保持编程规范的一致性。另一个好处是,在某些情况下,您可能需要改变关闭资源的顺序(不幸的是,不同的应用服务器和事务管理器具有不同的规则)。除了这些考虑,要使用全局事务,不需要改变您的SQL Map代码。
受管理的(Managed)全局事务
SQL Map框架也可以为您管理全局事务。要支持受管理的全局事务,必须在SQL Map配置文件中设定<transactionManager>的type属性为JTA,并设定“UserTransaction”属性为JNDI的全名,以使SqlMapClient实例能找到UserTransaction对象。
使用全局事务编程,代码没有多大的不同,但有几个小小的地方要注意。例如:
try {
orderSqlMap.startTransaction();
storeSqlMap.startTransaction();
orderSqlMap.insertOrder(…);
orderSqlMap.updateQuantity(…);
storeSqlMap.commitTransaction();
orderSqlMap.commitTransaction();
}
finally {
try {
storeSqlMap.endTransaction()
}
finally {
orderSqlMap.endTransaction()
}
}
警告!虽然这些看起来很简单,但记住不要滥用全局(分布式)事务,这点很重要。这样做既有性能方面的考虑,同时也是因为全局事务会让应用服务器和数据库驱动程序的设置变得更复杂。虽然看起来简单,您可能还是会遇到一些困难。
批处理
SQL Map API使用批处理很简单,可以使用两个简单的方法划分批处理的边界:
sqlMap.startBatch();
//…execute statements in between
sqlMap.executeBatch();
当调用endBatch()方法时,所有的批处理语句将通过JDBC Driver来执行。
用SqlMapClient执行SQL语句
SqlMapCient类提供了执行所有mapped statement的API。这些方法如下:
public int insert(String statementName, Object parameterObject) throws SQLException
public int update(String statementName, Object parameterObject) throws SQLException
public int delete(String statementName, Object parameterObject) throws SQLException
public Object queryForObject(String statementName, Object parameterObject) throws SQLException
public Object queryForObject(String statementName, Object parameterObject, Object resultObject) throws SQLException
public List queryForList(String statementName, Object parameterObject) throws SQLException
public List queryForList(String statementName, Object parameterObject, int skipResults, int maxResults) throws SQLException
public List queryForList (String statementName, Object parameterObject, RowHandler rowHandler) throws SQLException
public PaginatedList queryForPaginatedList(String statementName, Object parameterObject, int pageSize) throws SQLException
public Map queryForMap (String statementName, Object parameterObject, String keyProperty) throws SQLException
public Map queryForMap (String statementName, Object parameterObject, String keyProperty, String valueProperty) throws SQLException
代码例子
例子1:执行update(insert,update,delete)
sqlMap.startTransaction();
Product product = new Product();
product.setId (1);
product.setDescription (“Shih Tzu”);
int rows = sqlMap.insert (“insertProduct”, product);
sqlMap.commitTransaction();
例子2:查询成对象(select)
sqlMap.startTransaction();
Integer key = new Integer (1);
Product product = (Product)sqlMap.queryForObject (“getProduct”, key);
sqlMap.commitTransaction();
例子3:用预赋值的结果对象查询成对象(select)
sqlMap.startTransaction();
Customer customer = new Customer();
sqlMap.queryForObject(“getCust”, parameterObject, customer);
sqlMap.queryForObject(“getAddr”, parameterObject, customer);
sqlMap.commitTransaction();
例子4:查询成对象List(select)
sqlMap.startTransaction();
List list = sqlMap.queryForList (“getProductList”, null);
sqlMap.commitTransaction();
例子5:自动提交
//当没调用startTransaction的情况下,statements会自动提交。
//没必要commit/rollback。
int rows = sqlMap.insert (“insertProduct”, product);
例子6:用结果集边界查询成对象List(select)
sqlMap.startTransaction();
List list = sqlMap.queryForList (“getProductList”, null, 0, 40);
sqlMap.commitTransaction();
例子7:用RowHandler执行查询(select)
public class MyRowHandler implements RowHandler {
public void handleRow (Object object, List list) throws SQLException {
Product product = (Product) object;
product.setQuantity (10000);
sqlMap.update (“updateProduct”, product);
// Optionally you could add the result object to the list.
// The list is returned from the queryForList() method.
}
}
sqlMap.startTransaction();
RowHandler rowHandler = new MyRowHandler();
List list = sqlMap.queryForList (“getProductList”, null, rowHandler);
sqlMap.commitTransaction();
例子8:查询成Paginated List(select)
PaginatedList list =sqlMap.queryForPaginatedList (“getProductList”, null, 10);
list.nextPage();
list.previousPage();
例子9:查询成Map(select)
sqlMap.startTransaction();
Map map = sqlMap.queryForMap (“getProductList”, null, “productCode”);
sqlMap.commitTransaction();
Product p = (Product) map.get(“EST-93”);
三、在运用中的总结:
上面说的都是细节的东西,很多的人在学习的时候都是可以看到的,放在这里是为了以后方便查下ctrl+F嘛。不过,通过这些细节的学习会发现,在刚开始是在讲各个属性是干嘛用的,在稍微后面的时候就开始纠结怎么用才能使得效率更高一些。不可否认的是,属性的熟悉对我们很重要,但是后面的思想甚至是对数据库的了解才是最重要的,ibaits只不过是对数据库的操作的一个框架而已,我们应该话更多的时间在灵魂上!!
感觉ibatis上手还是比较容易的!