SQL MAP配置*

来源:互联网 发布:逃离中国知乎 编辑:程序博客网 时间:2024/05/22 06:48
 转自:

http://www.cnblogs.com/dwjaissk/archive/2006/03/07/344999.html

 

例如:<properties url="file:///c:/config/my.properties" />

(二) <setting> 

例如:<properties url="file:///c:/config/my.properties" />

(二) <setting>元素
<setting>元素用于配置和优化利用XML配置文件创建的SqlMapClient实例。<settings>元素及其所有的属性都是可选的。下面列出了<setting>元素支持的属性和功能:

1. maxExecute
同时执行一个Sql statement的最大线程数,大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。
例子:maxExecute="256"
缺省值:512

一般情况下,这个值要大于10,而且要同时比maxSessions和maxTransactions参数设定的值大。通常情况下,减少最大同时访问次数可以提高执行的效率。

2. maxSessions
是指在一个给定时间内处于活动状态的session(或客户端)的数量。这个值一般要大于或等于maxTransactions的参数值,同时要小于maxRequests的参数值
例子: maxSessions="64"
缺省值:128

3. maxTransaction
同时进入SqlMapClient.startTransaction()的最大线程数。大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。这个值应该总是小于或等于maxSessions,而且要比maxRequests小的多。通常情况下,减少这个值可以提高执行的效率。
例子:maxTransaction="16"
缺省值:32

4. cacheModelsEnabled
启用或禁用SqlMapClient所有的cache models。调试程序时有用。
例子:cacheModelsEnabled="true"
缺省值:true

5. lazyLoadingEnabled
启用或禁用SqlMapClient所有的lazy loading。调试程序时有用。
例子:lazyLoadingEnabled="true"
缺省值:true

6. enhancementEnabled
This setting enables runtime bytecode enhancement to facilitate optimized JavaBean property access as well as enhanced lazy loading.
例子 enhancementEnabled="true"
缺省值: false (disabled)

7. useStatementNamespaces
这个选项如果启用,你就必须使用全限定名来引用mapped statements,这个全名是由sqlMap的名字和statement的名字组成的。
例如:queryForObject("sqlMapName.statementName");

例子: useStatementNamespaces="false"
缺省值: false (disabled)

(三) <typeAlias>元素
<typeAlias>允许你指定别名。这样你就可以通过你指定的短名字来代替冗长的名字了。
例如:<typeAlias alias="shortname" type="com.long.class.path.Class"/>
下面是在SqlMap中预定义的别名:
1. Transaction Manager Aliases
JDBC  com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
JTA  com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
EXTERNAL com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig

2. Data Source Factory Aliases
SIMPLE  com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
DBCP  com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
JNDI  com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory

(四)<transactionManager>元素
注意:在Sql Map 1.0版本中,允许同时配置多个数据源,这样会导致一些问题。所以从2.0的版本开始,只允许配置一个数据源,如果你有配置多个数据源的需求的话,建议你使用多个不同配置的属性文件,或者在build Sql Map的时候,作为一个参数传进来。

<transactionManager>元素允许你为SQL Map配置事务管理服务。type属性用来指明使用哪种事务管理,值既可以是一个类名字,也可以是一个别名。

在框架中已经包含了三种事务管理:JDBC, JTA 和 EXTERNAL
 
1. JDBC
允许用JDBC通过Connection 的commit()和rollback()方法来控制事务。

2. JTA
这种事务管理使用一个JTA的全局事务将SQL Map的activities作为一个wider scope事务的一部分而包含进来,这个事务可能包含其他的数据库或事务
源。这个配置需要一个UserTransaction属性来通过JNDI resource设置本地的user transaction。

3. EXTERNAL
允许你自己管理事务。你仍然可以配置一个数据源,但是事务不会在框架生命周期中被提交或回滚。这意味着你必须要用自己的程序来控制事务。这个设置对于非事务型数据库是非常有用的。

(五) <datasource>元素
<datasource>标签及其属性是<transactionManager>的配置的一部分,它也是用于配置你的SQL Map所使用的数据源的。
目前本框架提供三种数据源工厂,但是你也可以自己写一个。下面是每一种数据源工厂的配置举例:

1. SimpleDataSourceFactory
SimpleDataSourceFactory 为池化的DataSource提供了一个基本的实现,适用于在没有容器提供数据源的情况。

<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"/>

  <!-- OPTIONAL PROPERTIES BELOW -->
  <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>

2. DbcpDataSourceFactory
DbcpDataSourceFactory 实现使用Jakarta DBCP(Database Connection Pool)的DataSource
API 提供连接池服务。适用于Web 容器不提供DataSource 服务的情况,或执行一个单独的
应用。DbcpDataSourceFactory 中必须要配置的参数例子如下:

<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}"/>
  <!-- OPTIONAL PROPERTIES BELOW -->
  <property name="Pool.MaximumActiveConnections" value="10"/>
  <property name="Pool.MaximumIdleConnections" value="5"/>
  <property name="Pool.MaximumWait" value="60000"/>
  <!-- Use of the validation query can be problematic.
  If you have difficulty, try without it. -->
  <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>

3. JndiDataSourceFactory
JndiDataSourceFactory 在应用服务器的容器中从JNDI Context 中查找DataSource 实现。当使用应用
服务器,并且服务器提供了容器管理的连接池和相关的DataSource 实现的情况下,可以使用
JndiDataSourceFactory。使用JDBC DataSource 的标准方法是通过JNDI 来查找。

JndiDataSourceFactory必须要配置的属性如下:
<transactionManager type="JDBC" >
 <dataSource type="JNDI">
  <property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
 </dataSource>
</transactionManager>
注意:上面的配置是使用标准的JDBC事务管理。但是,在一个容器管理的数据源中,你也可能想为全局的事务做如下配置:
<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>

注意:UserTransaction属性指向一个JNDI的位置,你可以通过这个JNDI找到一个UserTransaction实例。这个在JTA的事务管理中是需要的,这样可以使你的SQL MAP参与到包含有其他数据库和事务源的事务中。

(六)<sql-map>元素
sqlMap元素用于包括SQL Map 映射文件和其他的SQL Map 配置文件。每个SqlMapClient
对象使用的SQL Map 映射文件都要在此声明。映射文件作为stream resource 从类路径或URL中读
入。您必须相对于类路径或URL来指定所有的SQL Map 文件。
下面是几个例子:

<!-- CLASSPATH RESOURCES -->
<sqlMap resource="com/ibatis/examples/sql/Customer.xml" />
<sqlMap resource="com/ibatis/examples/sql/Account.xml" />
<sqlMap resource="com/ibatis/examples/sql/Product.xml" />

<!-- URL RESOURCES -->
<sqlMap url="file:///c:/config/Customer.xml " />
<sqlMap url="file:///c:/config/Account.xml " />
<sqlMap url="file:///c:/config/Product.xml" />

sqlMap所包含的标签:

<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>

The SQL Map XML File

( http://www.ibatis.com/dtd/sql-map-config-2.dtd)

一、Mapped Statements

Mapped statements可以是任何一个SQL statement,并且可以指定输入参数的map和输出结果的map。

简单的情况下,mapped statement可以直接指定一个类来做为输入参数和输出结果参数。mapped statement也可以使用cache model在内存中缓冲经常使用的数据。 

<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的配置中,放在[]中的配置参数是可选的,所以下面的Mapped Statement是完全合法的。

<statement id="insertTestProduct">

     insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, "Shih Tzu")

</statement>

下面逐一介绍各个标签的含义以及使用方法:

1. Statement 类型

下表中列出了所有的Statement,以及他们的属性和所支持的特征。

Statement Element

 

Attributes

 

Child Elements

 

Methods

 

<statement>

id

parameterClass

resultClass

parameterMap

resultMap

cacheModel

xmlResultName

All dynamic elements

insert

update

delete

All query methods

<insert>

 

id

parameterClass

parameterMap

All dynamic elements

<selectKey>

insert

update

delete

<update>

 

id

parameterClass

parameterMap

All dynamic elements

 

insert

update

delete

<delete>

 

id

parameterClass

parameterMap

All dynamic elements

insert

update

delete

<select>

 

id

parameterClass

resultClass

parameterMap

resultMap

cacheModel

All dynamic elements

All query methods

<procedure>

 

id

parameterClass

resultClass

parameterMap

resultMap

xmlResultName

All dynamic elements

insert

update

delete

All query methods

 

<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>

2. the SQL

sql语句无疑是map中最重要的组成部分,你可以使用任何的sql语句,只要你的数据库和JDBC驱动支持就行。你也可以使用数据库和驱动支持的任何函数。因为你是将sql语句写在了XML文档中,为了区别Sql中的"<>"和XML中的"<>",在写Sql时,可以用<![CDATA[ SQL ]]>来写。

3. 自增

很多关系型数据库都支持主键的自增,SQL Map可以通过<insert>标签中的<selectKey>来支持自增。预生成(如Oracle)和后生成(如Sql Server)都支持,下面是例子: 

<!—Oracle SEQUENCE Example -->

<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>

 

<!— Microsoft SQL Server IDENTITY Column Example -->

<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>

 

4. 存储过程:

通过<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>

 

调用上面的存储过程会在数据表的两列间交换Email地址,而且在对应的参数对象中的也会交换。记住:当parameter mapping的mode为INOUT或OUT的时候,

你输入的参数对象才会改变。很显然,不变的参数对象也是不会改变的,比如说String对象。

 

记住:一定要使用标准的JDBC存储过程的语法。请参见JDBC CallableStatement documentation以获取更多信息。

statement中的参数简介:

1. parameterClass

parameterClass 属性的值是Java类的全限定名(即包括类的包名)。parameterClass属性是可选的,目的是限制输入参数的类型为指定的Java 类。虽然Parameter-class属性是可选的,建议你为每一个SQL都指定parameterClass。如果不指定parameterClass 参数,任何带有合适属性(get/set 方法)的Java Bean 都可以作为输入参数。如果你使用了parameterMap, 那么你就不需要再使用parameterClass属性了。

下面是例子:

例1:

<insert id="insertAuthor2" parameterClass="Author">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

 

在上面的语句中,你指定的parameterClass=Author,那么在你的Author类中要有name,age,telephone和address属性,并且要有相应的get和set方法

例2:

你可以使用基本类型作为parameterClass,如:

<delete id="deleteAuthor" parameterClass="int">

     delete from author WHERE auth_id = #id#

</delete>

例3:

你可以使用HashMap作为parameterClass,如:

<insert id="insertAuthor3" parameterClass="java.util.HashMap">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

这时候,在你调用insertAuthor3的时候,你首先应该给传入的Map对象赋值,调用代码如下:

     HashMap paramMap = new HashMap();

     paramMap.put("name", "作者三");

     paramMap.put("age",new Integer(31));

     paramMap.put("address","南京");

     paramMap.put("telephone","025-987654321");

     sqlMapClient.insert("insertAuthor3", paramMap);

 

2. parameterMap

parameterMap 定义一系列有次序的参数用于匹配PreparedStatement 的JDBC值符号。

parameterMap属性很少使用,parameterMap 属性的值等于指定的parameterMap元素的name属性值。通常(和缺省的)的方法是使用inline parameters。

 

注意!动态mapped statement 只支持inline parameter,不支持parameter map。关于动态mapped statement,将在后文中介绍。

 

例如:

<parameterMap id="authorParameter" class="Author">

     <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

     <parameter property="age" jdbcType="INTEGER" javaType="java.lang.Integer" mode="INOUT"/>

     <parameter property="telephone" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

     <parameter property="address" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<insert id="insertAuthor1" parameterMap="authorParameter">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)

</insert>

 

上面的例子中,parameterMap的参数按次序匹配SQL语句中的值符号(?)。因此,第一个"?"号将被"name"属性的值替换,而第二个"?"号将被"age"属性的值替换,依此类推。

 

记住:使用parameterMap的时候,SQL中的参数用"?"来代替,并且每个"?"的顺序要与parameterMap中的定义完全匹配;如果使用parameterClass,那么SQL中的参数用"#parameterName#"来代替,如果传入的参数类为Bean,那么要有get和set这个参数名的方法。

 

3. resultClass

resultClass 属性可以让您指定一个Java 类,根据ResultSetMetaData 将其自动映射到JDBC ResultSet。只要是JavaBean 的属性、方法名称和ResultSet的列名匹配,属性自动赋值列值。

 

例1:

<select id="getAuthor1" parameterClass="int" resultClass="Author">

    SELECT auth_id as id,auth_name as name,auth_age as age,auth_tel as telephone,auth_address as address FROM author WHERE auth_id = #id#

</select>

在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。

如果你写成:

<select id="getAuthor1" parameterClass="int" resultClass="Author">

    SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_id = #id#

</select>

那么在你的Author类中,要有auth_id,auth_name,auth_age,auth_tel,auth_address属性,并且要有相应的get和set方法。

例2:

你还可以使用基本类型作为resultClass,如:

<statement id="getAuthorNumber" resultClass="int">

     <![CDATA[SELECT count(auth_id) as totalAuthor FROM author]]>

</statement>

例3:

你还可以使用HashMap作为resultClass,如:

<select id="getAuthor4" resultClass="java.util.HashMap">

    SELECT a.auth_id as authorid,a.auth_name as authname,a.auth_age as authage,a.auth_tel as authtel,a.auth_address as authaddress,b.art_title as arttitle FROM author a, article b WHERE a.auth_id=b.art_author

</select>

下面是调用代码:

List authorList = (List)sqlMapClient.queryForList("getAuthor4",null);

showMethod("getAllAuthor");

for(int i=0;i<authorList.size();i++)

{

     HashMap authMap = (HashMap)authorList.get(i);

    System.out.println("auth_id="+authMap.get("authid")+"; auth_name="+authMap.get("authname")+"; auth_age="+authMap.get("authage")+"; auth_tel="+authMap.get("authtel")+"; auth_address="+authMap.get("authaddress")+";auth_article="+authMap.get("arttitle"));

}

但是,使用resultClass 的自动映射存在一些限制,无法指定输出字段的数据类型,无法自动装入相关的数据(复杂属性),并且因为需要ResultSetMetaData的信息,会对性能有轻微的不利影响。但使用resultMap,这些限制都可以很容易解决。

4. resultMap:

使用resultMap 属性可以控制数据如何从结果集中取出,以及哪一个属性匹配哪一个字段。不象上面使用resultClass 属性的自动映射方法,resultMap属性可以允许指定字段的数据类型,NULL 的替代值。

例如:

<resultMap id="authorResult" class="Author">

     <result property="id" column="auth_id"/> 

     <result property="age" column="auth_age"/>

     <result property="name" column="auth_name"/>

     <result property="telephone" column="auth_tel"/>

     <result property="address" column="auth_address"/>

</resultMap> 

<select id="getAuthor3" resultMap="authorResult">

    SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

<statement id="getAllAuthor" resultMap="authorResult">

     SELECT * FROM author

</statement> 

在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。

 

通过resultMap 的定义,查询语句得到的ResultSet 被映射成Author对象。resultMap定义"id"属性值将赋予"auth_id"字段值,而"telephone"属性值将赋予"auth_tel"字段值,依次类推。

注意:在resultMap中所指定的字段必须是下面的select中的子集。

也就是说,你不能写成

<select id="getAuthor3" resultMap="authorResult">

     SELECT auth_address FROM author WHERE auth_address like #%address%#

</select>

但是你可以在resultMap中去掉<result property="id" column="auth_id"/>这行,仍然可以执行下面的语句:

<select id="getAuthor3" resultMap="authorResult">

     SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

这样的话,你就无法取得auth_id的值。 

5. cacheModel

定义查询mapped statement 的缓存。每一个查询mapped statement 可以使用不同或相同的cacheModel。

<cacheModel id="author-cache" imlementation="LRU">

     <flushInterval hours="24"/>

     <flushOnExecute statement="insertProduct"/>

     <flushOnExecute statement="updateProduct"/>

     <flushOnExecute statement="deleteProduct"/>

     <property name="size" value="1000" />

</cacheModel>

<select id="getAuthor3" resultMap="authorResult" cacheModel="author-cache">

     SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

上面的配置说明:"getAuthor3"的缓存使用WEAK引用类型,当你通过调用"getAuthor3"的时候,Ibatis将会把结果缓存起来。每24 小时缓存刷新一次,或当更新的操作(即上面配置的insertProduct、updateProduct或deleteProduct)发生时刷新。

当你对某些表中的记录操作频繁时,可以考虑使用缓冲,但是如果数据量过大的话,最好另想办法。

6. xmlResultName

当映射结果指向一个XML文档的时候,xmlResultName的值是指那个XML文档的root标签的名字。例如:

<select id="getAuthor1" parameterClass="int" resultClass="Author" xmlResultName="author">

    SELECT auth_id as id,auth_name as name,auth_age as age,auth_tel as telephone,auth_address as address FROM author WHERE auth_id = #id#

</select>

上面的select将产生如下的XML对象:

<author>

     <id>1</id>

     <name>作者三</name>

     <age>31</age>

     <telephone>025-987654321</telephone>

     <address>南京</address>

</author>

五、Parameter Maps and Inline Parameters

<parameterMap id="parameterMapName" [class="Author"]>

         <parameter property ="propertyName" [jdbcType="VARCHAR"] [javaType="string"]

         [nullValue="NUMERIC"] [null="-9999999"]/>

         <parameter …… />

         <parameter …… />

</parameterMap>

括号[]中是可选的属性。parameterMap 元素的id 属性作为唯一标识,在同一个SQL Map XML 文件中不能重名。一个parameterMap 可包含任意多的property 元素。

(一) property

property属性是指传入mapped statement中的JavaBean参数对象的属性名。这个属性名可以使用多次,这要看在这个statement中,这个属性名要出现多少次。例如:

<parameterMap id="authorParameter3" class="Author">

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<update id="updateAuthor2" parameterMap="authorParameter2">

         UPDATE author set auth_name=? WHERE auth_name = ?

</update>

但是如果使用这样的方法的话,调用代码应该是:

Author author = new Author();

author.setName("作者三");

sqlMapClient.update("updateAuthor2", paraMap);

那么它其实执行的是:

UPDATE author set auth_name='作者三' WHERE auth_name = '作者三'

这样的话,就根本没有了意义,因为,你只能传进一个Author对象,而这个Author对象的name属性值将会被用在整个Sql语句中,而一般的情况下是不应该相同的,也就是说,我们的本意可能是想:

UPDATE author set auth_name='作者N' WHERE auth_name = '作者三'

方法倒是有,不过我觉得不太好。

<parameterMap id="authorParameter2" class="java.util.HashMap">

         <parameter property="name1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

         <parameter property="name2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<update id="updateAuthor2" parameterMap="authorParameter2">

         UPDATE author set auth_name=? WHERE auth_name = ?

</update>

调用代码为:

HashMap paraMap = new HashMap();

paraMap.put("name1", "作者N");

paraMap.put("name2", "作者三");

sqlMapClient.update("updateAuthor2", paraMap);

如果你想到更好的方法解决这个问题的话,请不吝赐教。

(二) jdbcType

jdbcType用于指明数据库的字段类型。如果不说明字段类型的话,一些JDBC驱动程序就无法确定要操作的字段类型。例如:PreparedStatement.setNull(int parameterIndex, int sqlType)方法,要求指定数据类型。如果不指定数据类型,某些Driver 可能指定为Types.Other 或Types.Null。但是,不能保证所有的Driver 都表现一致。对于这种情况,SQL Map API 允许使用parameterMap 元素的jdbcType 属性指定数据类型。

正常情况下,只有当字段可以为NULL或日期时间类型时才需要type 属性。因为Java 只有一个Date 类型(java.util.Date),而大多数SQL 数据库有多个-通常至少有3 种。因此,需要指定字段类型是DATE 还是DATETIME。

Type 属性可以是JDBC Types 类中定义的任意参数的字符串值。虽然如此,还是有某些类型不支持(即BLOB)。

注意!大多数JDBC Driver 只有在字段可以为NULL 时需要指定type 属性。因此,对于这些Driver,只是在字段可以为NULL 时才需要指定type 属性。

注意!当使用Oracle Driver 时,如果没有给可以为NULL 的字段指定type 属性,当试图给这些字段赋值NULL 时,会出现"Invalid column。 type"错误。

(三) javaType

javaType用于指明作为参数传递的java bean的属性的类型。通常情况下,这可以通过反射机制从java bean中获取类型,但是一些特定的映射,比如说MAP和XML的映射就无法将类型信息传递给框架了。如果java type没有设置而且框架无法获知类型的话,那么这个类型会被指定为Object。

(四) nullValue

属性 nullValue的值可以是对于property 类型来说合法的任意值,用于指定NULL 的替换值。就是说,当Java Bean的属性值等于指定值时,相应的字段将赋值NULL。这个特性允许在应用中给不支持null的数据类型(即int,double,float等)赋值null。当这些数据类型的属性值匹配nullValue值(即匹配-9999)时,NULL 将代替nullValue 值写入数据库。

例如:

<parameterMap id="authorParameter" class="Author">

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

         <parameter property="age" jdbcType="INTEGER" javaType="java.lang.Integer" nullValue="-999" mode="INOUT"/>

         <parameter property="telephone" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

        <parameter property="address" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

</parameterMap>

<insert id="insertAuthor1" parameterMap="authorParameter">

         INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)

</insert>

您可以在另一个SQL Map XML 文件中引用parameterMap。例如,要在另一个文件中引用上面的parameterMap,可以使用名称"Product.insert-product-param"。

(五) Inline Parameter Maps

使用Inline Parameter Maps,可以把Java Bean 的属性名称嵌在mapped-statement 的定义中(即直接写在SQL 语句中)。

例如:

<insert id="insertAuthor1" parameterClass="Author">

         INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

这样,在你的Author类中,要有name,age,telephone,address的属性以及相应的get和set方法,这样做可以避免使用另外定义parameterMap的麻烦。

你也可以在内嵌参数中指定数据类型和nullValue,例如:

<insert id="insertAuthor1" parameterClass="Author">

        INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name:VARCHAR:NO_ENTRY#,#age:INTEGER:-999#,#telephone:VARCHAR:NO_ENTRY#,#address:VARCHAR:NO_ENTRY#)

</insert>

注意!在内嵌参数中,要指定NULL 的替代值,必须要先指定数据类型。

注意!如需要在查询时也使用NULL 替代值,必须同时在resultMap 中定义。

注意!如果您需要指定很多的数据类型和NULL 替代值,可以使用外部的parameterMap元素,这样会使代码更清晰。

六、Result Maps

在SQL Map 架构中,Result Map 是极其重要的组件。在执行查询Mapped Statement 时,resultMap 负责将结果集的列值映射成Java Bean 的属性值。resultMap 的结构如下:

<resultMap id="resultMapName" class="some.domain.Class" [extends="parent-resultMap"]>

         <result property="propertyName" column="COLUMN_NAME"

                            [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"]

                            [nullValue="-999999"] [select="someOtherStatement"]

                            />

         <result ……/>

         <result ……/>

         <result ……/>

</resultMap>

括号[]中是可选的属性resultMap 也有class 属性,是Java 类的全限定名(即包括包的名称)或该类的别名。该Java 类初始化并根据定义填充数据。

Extends 是可选的属性,可以设定成以为基础的另外一个resultMap 的名字。和在Java 中继承一个类相似,父resultMap 的属性将作为子resultMap 的一部分。父resultMap 的属性总是加到子resultMap 属性的前面,并且父resultMap 必须要在子resultMap 之前定义。父resultMap 和子resultMap 的class 属性不一定要一致,它们可以没有任何关系。

resultMap 可以包括任意多的property 映射,将查询结果集的列值映射成Java Bean 的属性。属性的映射按它们在resultMap中定义的顺序进行。属性class 必须符合Java Bean 规范,每一属性都必须拥有get/set 方法。

注意!ResultSet 的列值按它们在resultMap 中定义的顺序读取。

(一) property

property属性是指从mapped statement中返回的JavaBean对象的属性名。这个属性名也可以使用多次。

(二) column

column属性值是ResultSet中的列名字,即字段名,取得的这个字段的值将赋给property所指的bean属性。

(三) columnIndex

可选属性,用于改善性能。属性columnIndex 的值是ResultSet 中用于赋值Java Bean属性的字段次序号。在99%的应用中,不太可能需要牺牲可读性来换取性能。使用columnIndex,某些JDBC Driver可以大幅提高性能,某些则没有任何效果。

(四) jdbcType

同ParameterMap中的jdbcType

(五) javaType

同ParameterMap中的javaType

(六) nullValue

属性nullValue指定数据库中NULL的替代值。因此,如果从ResultSet中读出NULL值,JavaBean属性将被赋值为属性nullValue指定的替代值。

如果数据库中存在NULLABLE 属性的字段,但您想在你的应用程序中用指定的常量代替NULL,您可以这样做:

<resultMap id="get-product-result" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name" nullValue="you have no name"/>

</resultMap>

在上例中,如果取得的记录中auth_name字段的值为NULL,那么在赋给java bean的时候,name属性将被赋为"you have no name"。

(七) select 复杂属性

如果在一个类与另一个类之间是关联关系的话,那么当你用JDBC取得记录的时候,这个关联关系是如何实现的呢?例如这样的关系:一个作者可能会有多个文章发表,那么作者与文章之间就是很强的关联关系,而且是一对多的关系,在Author的Bean中是这样写的:

public class Author

{

    private int id;

.....

    private List articleList;

         public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

         ... ...

       public List getArticleList()

    {

        return articleList;

    }

   

    public void setArticleList(List articleList)

    {

        this.articleList=articleList;

    }

}

当你执行一条sql语句从数据表author中取出相应的数据的时候,在上面的java bean中,articleList如何赋值呢?这时候,就需要使用select属性。

1.1:1关系:

我们先假设在author和article之间使用1:1的关系,虽然在真实世界中是不正确的,我们只是做个例子,那么Author和article的bean代码如下:

public class Author

{

    private int id;

    private int age;

    private String name;  

    private String address;

    private String telephone;

    private Article article;

      

    public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age=age;

    }

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name=name;

    }

    public String getAddress()

    {

        return address;

    }

    public void setAddress(String address)

    {

        this.address=address;

    }

    public String getTelephone()

    {

        return telephone;

    }

    public void setTelephone(String telephone)

    {

        this.telephone=telephone;

    }

 

         public Article getArticle()

    {

        return this.article;

    }

   

    public void setArticle(Article article)

    {

        this.article=article;

    }

public class Article

{

    private int id;

    private String title;

    private Date createtime;

    private int author;

   

    public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

    public String getTitle()

    {

        return title;

    }

    public void setTitle(String title)

    {

        this.title=title;

    }

    public Date getCreatetime()

    {

        return createtime;

    }

    public void setCreatetime(Date createtime)

    {

        this.createtime=createtime;

    }

    public int getAuthor()

    {

        return author;

    }

    public void setAuthor(int author)

    {

        this.author=author;

    }

}

在author.xml的配置如下:

<resultMap id="linkResultMap1" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article" column="auth_id" select="getLinkArticle1"/>

</resultMap>

<select id="getAuthor5" resultMap="linkResultMap1" parameterClass="int">

         SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle1" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id#

</select>

调用代码如下:

Author author = (Author)sqlMapClient.queryForObject("getAuthor5", new Integer(1));

System.out.println(author.getName()+"'s article is :"+author.getArticle().getTitle());

你可以看到,对于Author类中的article属性,IBatis是将取得的记录的auth_id字段值作为参数传入到id="getLinkArticle1"的语句中,并将取得的结果封装成Article对象,并赋给Author的article属性。上面的调用代码实际执行的两条sql语句是:

SELECT * FROM author WHERE auth_id = 1

SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = 1

在第二条语句中,将取得的记录封装成Article对象,并赋给Author的article属性,所以,你可以直接使用author.getArticle().getTitle()获得文章的标题。

上面的方法显示了如何实现1:1的关联关系,但是上面的方法并不好,原因是可能会执行很多次查询!

(1)避免 N+1 Selects (1:1)

如果上面的配置如下:

<resultMap id="linkResultMap1" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article" column="auth_id" select="getLinkArticle1"/>

</resultMap>

<select id="getAuthor5" resultMap="linkResultMap1" parameterClass="int">

         SELECT * FROM author WHERE auth_id > #id#

</select>

<select id="getLinkArticle1" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id#

</select>

调用代码如下:

Author author = (Author)sqlMapClient.queryForList("getAuthor5", new Integer(1));

如果SELECT * FROM author WHERE auth_id > 1的记录有N条,那么将对id="getLinkArticle1"的语句执行N次查询,这样所有的查询总数将为N+1次,执行效率会很低。

这时,可以使用下面的联合查询的方法来解决:

<resultMap id="linkResultMap2" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article.title" column="art_title"/>

</resultMap>

<select id="getAuthor6" resultMap="linkResultMap2" parameterClass="int">

         <![CDATA[ SELECT a.auth_id,a.auth_age,a.auth_name,a.auth_tel,a.auth_address,b.art_title FROM author a,article b WHERE a.auth_id > #id# and a.auth_id = b.art_id]]>

</select>

调用代码为:

Author author = (Author)sqlMapClient.queryForList("getAuthor6", new Integer(1));

这样只用一条Sql语句就可以解决。

2.1:M与M:N关系:

下面我们讨论1:M的关系,一个author可能有多个article,所以,author与article之间是一对多的关系。那么我们在Author类中加入如下代码(在省略号间的是要加入的代码):

public class Author

{

... ...

         private List articleList;

    public List getArticleList()

    {

        return articleList;

    }

   

    public void setArticleList(List articleList)

    {

        this.articleList=articleList;

    }

... ...

}

配置如下:

<resultMap id="linkResultMap3" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="articleList" column="auth_id" select="getLinkArticle3"/>

</resultMap>

<select id="getAuthor7" resultMap="linkResultMap3" parameterClass="int">

         SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle3" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id#

</select>

调用代码为:

Author author = (Author)sqlMapClient.queryForObject("getAuthor7", new Integer(1));

System.out.println(author.getName()+"的文章有:");

for(int i=0;i<author.getArticleList().size();i++)

{

         int num=i+1;

         Article art = (Article)author.getArticleList().get(i);

         System.out.println(num+". "+art.getTitle());

}

从上面的实现可以看出,你只需要在bean中加入一个java.util.List(或java.util.Collection)类型的articleList来表示所有的文章列表即可,调用部分没有什么变化,IBaits会自动从Article中取得的记录封装成Article对象并加入到一个List对象中,然后将这个List对象赋值给Author类的articleList属性。

(1)避免 N+1 Selects (1:M and M:N)

1:M和M:N的情况与1:1的情况相似,也会出现N+1 Selects 的情况,但是到目前位置,还没有其他的方法来解决这个问题,希望在不久的将来能够解决。

3.多个复杂参数属性

你可能已经注意到了,上面的例子中,在resultMap中只指明了一个column属性用于id=”getLinkArticle”的statement关联。其实,Ibatis允许你指明多个column属性与id=”getLinkArticle”的statement关联,语法很简单, {param1=column1, param2=column2, …, paramN=columnN}。下面是一个例子:

<resultMap id="linkResultMap4" class="Author">

    <result property="id" column="auth_id"/>   

    <result property="age" column="auth_age"/>

    <result property="name" column="auth_name"/>

    <result property="telephone" column="auth_tel"/>

    <result property="address" column="auth_address"/>

    <result property="articleList" column="{id=auth_id,address=auth_address}" select="getLinkArticle4"/>

</resultMap>

<select id="getAuthor8" resultMap="linkResultMap4" parameterClass="int">

    SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle4" resultClass="com.ibatis.beans.Article" parameterClass="int">

    SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id# and art_publish_add=#address#

</select>

你也可以只写字段的名称,只要按照所关联的statement中所对应的字段顺序即可,象这样:

{auth_id,auth_address}

上面的这个例子我在Mysql的环境中运行没有通过,报出的错误是:Column'{auth_id,auth_address}' not found.

这个错误是与JDBC驱动无关的,而是在做XML解析的时候发生的错误,不知道是什么原因,如果您有这样的成功经历的话,希望能共同分享,我的Email是:tenwang1977@163.com

注意:有些JDBC驱动不支持同时打开多个ResultSets(单个连接)。所以说,这样的驱动不能完成复杂对象的映射,因为JDBC驱动需要多个ResultSets的连接,这时候,只能使用一个关联查询解决问题。

如果你使用Microsoft SQL Server 2000 的JDBC驱动的话,你需要在配置url的时候,在url后加上SelectMethod=Cursor

4.在Parameter Maps and Result Maps中支持的参数

Java Type

JavaBean/Map

Property Mapping

Result Class /

Parameter Class***

Type Alias**

boolean

YES

NO

boolean

java.lang.Boolean

YES

YES

boolean

byte

YES

NO

byte

java.lang.Byte

YES

YES

byte

short

YES

NO

short

java.lang.Short

YES

YES

short

int

YES

NO

Int/ Integer

java.lang.Integer

YES

YES

Int/ Integer

long

YES

NO

long

java.lang.Long

YES

YES

long

float

YES

NO

float

java.lang.Float

YES

YES

float

double

YES

NO

double

java.lang.Double

YES

YES

double

java.lang.String

YES

YES

string

java.util.Date

YES

YES

date

java.math.BigDecimal

YES

YES

decimal

* java.sql.Date

YES

YES

N/A

* java.sql.Time

YES

YES

N/A

* java.sql.Timestamp

YES

YES

N/A

七、缓存Mapped Statement Result

<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>

上面的cache model 创建了一个名为“product-cache”的缓存,使用“最近最少使用”(LRU)实现,每24小时,缓冲区将刷新一次,而且在执行insertProduct、updateProduct和deleteProduct的statement时,缓冲区也将刷新,设定的时间可以设定为hours,minutes, seconds或 milliseconds。一些Cache的实现需要附加的属性,比如说上例中的cache-size

 属性,cache的大小指明了可以存放在cache中的实体的个数。type属性的名称要么是全限定的类名,要么是缓存实现的别名。Cache Model 使用插件的形式来支持不同的缓存算法。它的实现在cache-model 元素的type属性中指定(如上所示)。

(一)Read-Only 与 Read/Write

Ibatis支持只读和可读写的Cache,只读的Cache可以在所有的用户间共享,所以它可以提供更大的操作空间。但是从只读缓冲中读取的对象不能够被修改。如果你要对你取得的对象进行修改的话,那么你只能用可读写的缓冲。readOnly=”true”为只读缓冲;readOnly=”false”为可读写缓冲。

(二)Serializable Read/Write Caches

要使用Serializable Read/Write Caches,设置readOnly=”false”serialize=”true”。默认情况下,采用的是readOnly=” true”serialize=”false”。

(三)缓冲类型

目前包括以下的4 个缓冲类型实现:

1. “MEMORY” (com.ibatis.db.sqlmap.cache.memory.MemoryCacheController)

MEMORY cache 实现使用reference 类型来管理cache 的行为。垃圾收集器可以根据reference 类型判断是否要回收cache 中的数据。MEMORY 实现适用于没有统一的对象重用模式的应用,或内存不足的应用。

MEMORY 实现可以这样配置:

<cache-model name="product-cache" implementation ="MEMORY">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”reference-type” value=”WEAK” />

</cache-model>

MEMORY cache 实现只认识一个<cache-property>元素。这个名为“reference-type”属性的值必须是STRONG,SOFT 和WEAK 三者其一。这三个值分别对应于JVM 不同的内存reference 类型。

(1) WEAK(缺省)

大多数情况下,WEAK类型是最佳选择。如果不指定类型,缺省类型就是WEAK。它能大大提高常用查询的性能。但是对于当前不被使用的查询结果数据,将被清除以释放内存用来分配其他对象。

(2) SOFT

在查询结果对象数据不被使用,同时需要内存分配其他对象的情况下,SOFT类型将减少内存不足的可能性。然而,这不是最具侵入性的reference类型,结果数据依然可能被清除。

(3) STRONG

确保查询结果数据一直保留在内存中,除非Cache被刷新(例如,到了刷新的时间或执行了更新数据的操作)。

对于下面的情况,这是理想的选择:

1” 结果内容数据很少

2” 完全静态的数据

3” 频繁使用的数据

优点是对于这类查询性能非常好。缺点是,如果需要分配其他对象,内存无法释放(可能是更重要的数据对象)。

2. “LRU” (com.ibatis.db.sqlmap.cache.lru.LruCacheController)

LRU Cache 实现用“最近最少使用”原则来确定如何从Cache 中清除对象。当Cache溢出时,最近最少使用的对象将被从Cache 中清除。

<cache-model name="product-cache" implementation ="LRU">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”cache-size” value=”1000” />

</cache-model>

值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。

3. “FIFO” (com.ibatis.db.sqlmap.cache.fifo.FifoCacheController)

FIFO Cache 实现用“先进先出”原则来确定如何从Cache 中清除对象。对于短时间内持续引用特定的查询而后很可能不再使用的情况,FIFO Cache 是很好的选择。

<cache-model name="product-cache" implementation ="FIFO">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”cache-size” value=”1000” />

</cache-model>

值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。

4. “OSCACHE” (com.ibatis.db.sqlmap.cache.oscache.OSCacheController)

OSCACHE Cache 实现是OSCache2.0 缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。

<cache-model name="product-cache" implementation ="OSCACHE">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

</cache-model>

OSCACHE 实现不使用cache-property 元素。而是在类路径的根路径中使用标准的oscache.properties 文件进行配置。在oscache.properties 文件中,您可以配置Cache 的算法(和上面讨论的算法很类似),Cache 的大小,持久化方法(内存,文件等)和集群方法。

元素
<setting>元素用于配置和优化利用XML配置文件创建的SqlMapClient实例。<settings>元素及其所有的属性都是可选的。下面列出了<setting>元素支持的属性和功能:

1. maxExecute
同时执行一个Sql statement的最大线程数,大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。
例子:maxExecute="256"
缺省值:512

一般情况下,这个值要大于10,而且要同时比maxSessions和maxTransactions参数设定的值大。通常情况下,减少最大同时访问次数可以提高执行的效率。

2. maxSessions
是指在一个给定时间内处于活动状态的session(或客户端)的数量。这个值一般要大于或等于maxTransactions的参数值,同时要小于maxRequests的参数值
例子: maxSessions="64"
缺省值:128

3. maxTransaction
同时进入SqlMapClient.startTransaction()的最大线程数。大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。这个值应该总是小于或等于maxSessions,而且要比maxRequests小的多。通常情况下,减少这个值可以提高执行的效率。
例子:maxTransaction="16"
缺省值:32

4. cacheModelsEnabled
启用或禁用SqlMapClient所有的cache models。调试程序时有用。
例子:cacheModelsEnabled="true"
缺省值:true

5. lazyLoadingEnabled
启用或禁用SqlMapClient所有的lazy loading。调试程序时有用。
例子:lazyLoadingEnabled="true"
缺省值:true

6. enhancementEnabled
This setting enables runtime bytecode enhancement to facilitate optimized JavaBean property access as well as enhanced lazy loading.
例子 enhancementEnabled="true"
缺省值: false (disabled)

7. useStatementNamespaces
这个选项如果启用,你就必须使用全限定名来引用mapped statements,这个全名是由sqlMap的名字和statement的名字组成的。
例如:queryForObject("sqlMapName.statementName");

例子: useStatementNamespaces="false"
缺省值: false (disabled)

(三) <typeAlias>元素
<typeAlias>允许你指定别名。这样你就可以通过你指定的短名字来代替冗长的名字了。
例如:<typeAlias alias="shortname" type="com.long.class.path.Class"/>
下面是在SqlMap中预定义的别名:
1. Transaction Manager Aliases
JDBC  com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
JTA  com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
EXTERNAL com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig

2. Data Source Factory Aliases
SIMPLE  com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
DBCP  com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
JNDI  com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory

(四)<transactionManager>元素
注意:在Sql Map 1.0版本中,允许同时配置多个数据源,这样会导致一些问题。所以从2.0的版本开始,只允许配置一个数据源,如果你有配置多个数据源的需求的话,建议你使用多个不同配置的属性文件,或者在build Sql Map的时候,作为一个参数传进来。

<transactionManager>元素允许你为SQL Map配置事务管理服务。type属性用来指明使用哪种事务管理,值既可以是一个类名字,也可以是一个别名。

在框架中已经包含了三种事务管理:JDBC, JTA 和 EXTERNAL
 
1. JDBC
允许用JDBC通过Connection 的commit()和rollback()方法来控制事务。

2. JTA
这种事务管理使用一个JTA的全局事务将SQL Map的activities作为一个wider scope事务的一部分而包含进来,这个事务可能包含其他的数据库或事务
源。这个配置需要一个UserTransaction属性来通过JNDI resource设置本地的user transaction。

3. EXTERNAL
允许你自己管理事务。你仍然可以配置一个数据源,但是事务不会在框架生命周期中被提交或回滚。这意味着你必须要用自己的程序来控制事务。这个设置对于非事务型数据库是非常有用的。

(五) <datasource>元素
<datasource>标签及其属性是<transactionManager>的配置的一部分,它也是用于配置你的SQL Map所使用的数据源的。
目前本框架提供三种数据源工厂,但是你也可以自己写一个。下面是每一种数据源工厂的配置举例:

1. SimpleDataSourceFactory
SimpleDataSourceFactory 为池化的DataSource提供了一个基本的实现,适用于在没有容器提供数据源的情况。

<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"/>

  <!-- OPTIONAL PROPERTIES BELOW -->
  <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>

2. DbcpDataSourceFactory
DbcpDataSourceFactory 实现使用Jakarta DBCP(Database Connection Pool)的DataSource
API 提供连接池服务。适用于Web 容器不提供DataSource 服务的情况,或执行一个单独的
应用。DbcpDataSourceFactory 中必须要配置的参数例子如下:

<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}"/>
  <!-- OPTIONAL PROPERTIES BELOW -->
  <property name="Pool.MaximumActiveConnections" value="10"/>
  <property name="Pool.MaximumIdleConnections" value="5"/>
  <property name="Pool.MaximumWait" value="60000"/>
  <!-- Use of the validation query can be problematic.
  If you have difficulty, try without it. -->
  <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>

3. JndiDataSourceFactory
JndiDataSourceFactory 在应用服务器的容器中从JNDI Context 中查找DataSource 实现。当使用应用
服务器,并且服务器提供了容器管理的连接池和相关的DataSource 实现的情况下,可以使用
JndiDataSourceFactory。使用JDBC DataSource 的标准方法是通过JNDI 来查找。

JndiDataSourceFactory必须要配置的属性如下:
<transactionManager type="JDBC" >
 <dataSource type="JNDI">
  <property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
 </dataSource>
</transactionManager>
注意:上面的配置是使用标准的JDBC事务管理。但是,在一个容器管理的数据源中,你也可能想为全局的事务做如下配置:
<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>

注意:UserTransaction属性指向一个JNDI的位置,你可以通过这个JNDI找到一个UserTransaction实例。这个在JTA的事务管理中是需要的,这样可以使你的SQL MAP参与到包含有其他数据库和事务源的事务中。

(六)<sql-map>元素
sqlMap元素用于包括SQL Map 映射文件和其他的SQL Map 配置文件。每个SqlMapClient
对象使用的SQL Map 映射文件都要在此声明。映射文件作为stream resource 从类路径或URL中读
入。您必须相对于类路径或URL来指定所有的SQL Map 文件。
下面是几个例子:

<!-- CLASSPATH RESOURCES -->
<sqlMap resource="com/ibatis/examples/sql/Customer.xml" />
<sqlMap resource="com/ibatis/examples/sql/Account.xml" />
<sqlMap resource="com/ibatis/examples/sql/Product.xml" />

<!-- URL RESOURCES -->
<sqlMap url="file:///c:/config/Customer.xml " />
<sqlMap url="file:///c:/config/Account.xml " />
<sqlMap url="file:///c:/config/Product.xml" />

sqlMap所包含的标签:

<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>

The SQL Map XML File

( http://www.ibatis.com/dtd/sql-map-config-2.dtd)

一、Mapped Statements

Mapped statements可以是任何一个SQL statement,并且可以指定输入参数的map和输出结果的map。

简单的情况下,mapped statement可以直接指定一个类来做为输入参数和输出结果参数。mapped statement也可以使用cache model在内存中缓冲经常使用的数据。 

<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的配置中,放在[]中的配置参数是可选的,所以下面的Mapped Statement是完全合法的。

<statement id="insertTestProduct">

     insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, "Shih Tzu")

</statement>

下面逐一介绍各个标签的含义以及使用方法:

1. Statement 类型

下表中列出了所有的Statement,以及他们的属性和所支持的特征。

Statement Element

 

Attributes

 

Child Elements

 

Methods

 

<statement>

id

parameterClass

resultClass

parameterMap

resultMap

cacheModel

xmlResultName

All dynamic elements

insert

update

delete

All query methods

<insert>

 

id

parameterClass

parameterMap

All dynamic elements

<selectKey>

insert

update

delete

<update>

 

id

parameterClass

parameterMap

All dynamic elements

 

insert

update

delete

<delete>

 

id

parameterClass

parameterMap

All dynamic elements

insert

update

delete

<select>

 

id

parameterClass

resultClass

parameterMap

resultMap

cacheModel

All dynamic elements

All query methods

<procedure>

 

id

parameterClass

resultClass

parameterMap

resultMap

xmlResultName

All dynamic elements

insert

update

delete

All query methods

 

<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>

2. the SQL

sql语句无疑是map中最重要的组成部分,你可以使用任何的sql语句,只要你的数据库和JDBC驱动支持就行。你也可以使用数据库和驱动支持的任何函数。因为你是将sql语句写在了XML文档中,为了区别Sql中的"<>"和XML中的"<>",在写Sql时,可以用<![CDATA[ SQL ]]>来写。

3. 自增

很多关系型数据库都支持主键的自增,SQL Map可以通过<insert>标签中的<selectKey>来支持自增。预生成(如Oracle)和后生成(如Sql Server)都支持,下面是例子: 

<!—Oracle SEQUENCE Example -->

<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>

 

<!— Microsoft SQL Server IDENTITY Column Example -->

<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>

 

4. 存储过程:

通过<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>

 

调用上面的存储过程会在数据表的两列间交换Email地址,而且在对应的参数对象中的也会交换。记住:当parameter mapping的mode为INOUT或OUT的时候,

你输入的参数对象才会改变。很显然,不变的参数对象也是不会改变的,比如说String对象。

 

记住:一定要使用标准的JDBC存储过程的语法。请参见JDBC CallableStatement documentation以获取更多信息。

statement中的参数简介:

1. parameterClass

parameterClass 属性的值是Java类的全限定名(即包括类的包名)。parameterClass属性是可选的,目的是限制输入参数的类型为指定的Java 类。虽然Parameter-class属性是可选的,建议你为每一个SQL都指定parameterClass。如果不指定parameterClass 参数,任何带有合适属性(get/set 方法)的Java Bean 都可以作为输入参数。如果你使用了parameterMap, 那么你就不需要再使用parameterClass属性了。

下面是例子:

例1:

<insert id="insertAuthor2" parameterClass="Author">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

 

在上面的语句中,你指定的parameterClass=Author,那么在你的Author类中要有name,age,telephone和address属性,并且要有相应的get和set方法

例2:

你可以使用基本类型作为parameterClass,如:

<delete id="deleteAuthor" parameterClass="int">

     delete from author WHERE auth_id = #id#

</delete>

例3:

你可以使用HashMap作为parameterClass,如:

<insert id="insertAuthor3" parameterClass="java.util.HashMap">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

这时候,在你调用insertAuthor3的时候,你首先应该给传入的Map对象赋值,调用代码如下:

     HashMap paramMap = new HashMap();

     paramMap.put("name", "作者三");

     paramMap.put("age",new Integer(31));

     paramMap.put("address","南京");

     paramMap.put("telephone","025-987654321");

     sqlMapClient.insert("insertAuthor3", paramMap);

 

2. parameterMap

parameterMap 定义一系列有次序的参数用于匹配PreparedStatement 的JDBC值符号。

parameterMap属性很少使用,parameterMap 属性的值等于指定的parameterMap元素的name属性值。通常(和缺省的)的方法是使用inline parameters。

 

注意!动态mapped statement 只支持inline parameter,不支持parameter map。关于动态mapped statement,将在后文中介绍。

 

例如:

<parameterMap id="authorParameter" class="Author">

     <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

     <parameter property="age" jdbcType="INTEGER" javaType="java.lang.Integer" mode="INOUT"/>

     <parameter property="telephone" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

     <parameter property="address" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<insert id="insertAuthor1" parameterMap="authorParameter">

     INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)

</insert>

 

上面的例子中,parameterMap的参数按次序匹配SQL语句中的值符号(?)。因此,第一个"?"号将被"name"属性的值替换,而第二个"?"号将被"age"属性的值替换,依此类推。

 

记住:使用parameterMap的时候,SQL中的参数用"?"来代替,并且每个"?"的顺序要与parameterMap中的定义完全匹配;如果使用parameterClass,那么SQL中的参数用"#parameterName#"来代替,如果传入的参数类为Bean,那么要有get和set这个参数名的方法。

 

3. resultClass

resultClass 属性可以让您指定一个Java 类,根据ResultSetMetaData 将其自动映射到JDBC ResultSet。只要是JavaBean 的属性、方法名称和ResultSet的列名匹配,属性自动赋值列值。

 

例1:

<select id="getAuthor1" parameterClass="int" resultClass="Author">

    SELECT auth_id as id,auth_name as name,auth_age as age,auth_tel as telephone,auth_address as address FROM author WHERE auth_id = #id#

</select>

在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。

如果你写成:

<select id="getAuthor1" parameterClass="int" resultClass="Author">

    SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_id = #id#

</select>

那么在你的Author类中,要有auth_id,auth_name,auth_age,auth_tel,auth_address属性,并且要有相应的get和set方法。

例2:

你还可以使用基本类型作为resultClass,如:

<statement id="getAuthorNumber" resultClass="int">

     <![CDATA[SELECT count(auth_id) as totalAuthor FROM author]]>

</statement>

例3:

你还可以使用HashMap作为resultClass,如:

<select id="getAuthor4" resultClass="java.util.HashMap">

    SELECT a.auth_id as authorid,a.auth_name as authname,a.auth_age as authage,a.auth_tel as authtel,a.auth_address as authaddress,b.art_title as arttitle FROM author a, article b WHERE a.auth_id=b.art_author

</select>

下面是调用代码:

List authorList = (List)sqlMapClient.queryForList("getAuthor4",null);

showMethod("getAllAuthor");

for(int i=0;i<authorList.size();i++)

{

     HashMap authMap = (HashMap)authorList.get(i);

    System.out.println("auth_id="+authMap.get("authid")+"; auth_name="+authMap.get("authname")+"; auth_age="+authMap.get("authage")+"; auth_tel="+authMap.get("authtel")+"; auth_address="+authMap.get("authaddress")+";auth_article="+authMap.get("arttitle"));

}

但是,使用resultClass 的自动映射存在一些限制,无法指定输出字段的数据类型,无法自动装入相关的数据(复杂属性),并且因为需要ResultSetMetaData的信息,会对性能有轻微的不利影响。但使用resultMap,这些限制都可以很容易解决。

4. resultMap:

使用resultMap 属性可以控制数据如何从结果集中取出,以及哪一个属性匹配哪一个字段。不象上面使用resultClass 属性的自动映射方法,resultMap属性可以允许指定字段的数据类型,NULL 的替代值。

例如:

<resultMap id="authorResult" class="Author">

     <result property="id" column="auth_id"/> 

     <result property="age" column="auth_age"/>

     <result property="name" column="auth_name"/>

     <result property="telephone" column="auth_tel"/>

     <result property="address" column="auth_address"/>

</resultMap> 

<select id="getAuthor3" resultMap="authorResult">

    SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

<statement id="getAllAuthor" resultMap="authorResult">

     SELECT * FROM author

</statement> 

在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。

 

通过resultMap 的定义,查询语句得到的ResultSet 被映射成Author对象。resultMap定义"id"属性值将赋予"auth_id"字段值,而"telephone"属性值将赋予"auth_tel"字段值,依次类推。

注意:在resultMap中所指定的字段必须是下面的select中的子集。

也就是说,你不能写成

<select id="getAuthor3" resultMap="authorResult">

     SELECT auth_address FROM author WHERE auth_address like #%address%#

</select>

但是你可以在resultMap中去掉<result property="id" column="auth_id"/>这行,仍然可以执行下面的语句:

<select id="getAuthor3" resultMap="authorResult">

     SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

这样的话,你就无法取得auth_id的值。 

5. cacheModel

定义查询mapped statement 的缓存。每一个查询mapped statement 可以使用不同或相同的cacheModel。

<cacheModel id="author-cache" imlementation="LRU">

     <flushInterval hours="24"/>

     <flushOnExecute statement="insertProduct"/>

     <flushOnExecute statement="updateProduct"/>

     <flushOnExecute statement="deleteProduct"/>

     <property name="size" value="1000" />

</cacheModel>

<select id="getAuthor3" resultMap="authorResult" cacheModel="author-cache">

     SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%#

</select>

上面的配置说明:"getAuthor3"的缓存使用WEAK引用类型,当你通过调用"getAuthor3"的时候,Ibatis将会把结果缓存起来。每24 小时缓存刷新一次,或当更新的操作(即上面配置的insertProduct、updateProduct或deleteProduct)发生时刷新。

当你对某些表中的记录操作频繁时,可以考虑使用缓冲,但是如果数据量过大的话,最好另想办法。

6. xmlResultName

当映射结果指向一个XML文档的时候,xmlResultName的值是指那个XML文档的root标签的名字。例如:

<select id="getAuthor1" parameterClass="int" resultClass="Author" xmlResultName="author">

    SELECT auth_id as id,auth_name as name,auth_age as age,auth_tel as telephone,auth_address as address FROM author WHERE auth_id = #id#

</select>

上面的select将产生如下的XML对象:

<author>

     <id>1</id>

     <name>作者三</name>

     <age>31</age>

     <telephone>025-987654321</telephone>

     <address>南京</address>

</author>

五、Parameter Maps and Inline Parameters

<parameterMap id="parameterMapName" [class="Author"]>

         <parameter property ="propertyName" [jdbcType="VARCHAR"] [javaType="string"]

         [nullValue="NUMERIC"] [null="-9999999"]/>

         <parameter …… />

         <parameter …… />

</parameterMap>

括号[]中是可选的属性。parameterMap 元素的id 属性作为唯一标识,在同一个SQL Map XML 文件中不能重名。一个parameterMap 可包含任意多的property 元素。

(一) property

property属性是指传入mapped statement中的JavaBean参数对象的属性名。这个属性名可以使用多次,这要看在这个statement中,这个属性名要出现多少次。例如:

<parameterMap id="authorParameter3" class="Author">

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<update id="updateAuthor2" parameterMap="authorParameter2">

         UPDATE author set auth_name=? WHERE auth_name = ?

</update>

但是如果使用这样的方法的话,调用代码应该是:

Author author = new Author();

author.setName("作者三");

sqlMapClient.update("updateAuthor2", paraMap);

那么它其实执行的是:

UPDATE author set auth_name='作者三' WHERE auth_name = '作者三'

这样的话,就根本没有了意义,因为,你只能传进一个Author对象,而这个Author对象的name属性值将会被用在整个Sql语句中,而一般的情况下是不应该相同的,也就是说,我们的本意可能是想:

UPDATE author set auth_name='作者N' WHERE auth_name = '作者三'

方法倒是有,不过我觉得不太好。

<parameterMap id="authorParameter2" class="java.util.HashMap">

         <parameter property="name1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

         <parameter property="name2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<update id="updateAuthor2" parameterMap="authorParameter2">

         UPDATE author set auth_name=? WHERE auth_name = ?

</update>

调用代码为:

HashMap paraMap = new HashMap();

paraMap.put("name1", "作者N");

paraMap.put("name2", "作者三");

sqlMapClient.update("updateAuthor2", paraMap);

如果你想到更好的方法解决这个问题的话,请不吝赐教。

(二) jdbcType

jdbcType用于指明数据库的字段类型。如果不说明字段类型的话,一些JDBC驱动程序就无法确定要操作的字段类型。例如:PreparedStatement.setNull(int parameterIndex, int sqlType)方法,要求指定数据类型。如果不指定数据类型,某些Driver 可能指定为Types.Other 或Types.Null。但是,不能保证所有的Driver 都表现一致。对于这种情况,SQL Map API 允许使用parameterMap 元素的jdbcType 属性指定数据类型。

正常情况下,只有当字段可以为NULL或日期时间类型时才需要type 属性。因为Java 只有一个Date 类型(java.util.Date),而大多数SQL 数据库有多个-通常至少有3 种。因此,需要指定字段类型是DATE 还是DATETIME。

Type 属性可以是JDBC Types 类中定义的任意参数的字符串值。虽然如此,还是有某些类型不支持(即BLOB)。

注意!大多数JDBC Driver 只有在字段可以为NULL 时需要指定type 属性。因此,对于这些Driver,只是在字段可以为NULL 时才需要指定type 属性。

注意!当使用Oracle Driver 时,如果没有给可以为NULL 的字段指定type 属性,当试图给这些字段赋值NULL 时,会出现"Invalid column。 type"错误。

(三) javaType

javaType用于指明作为参数传递的java bean的属性的类型。通常情况下,这可以通过反射机制从java bean中获取类型,但是一些特定的映射,比如说MAP和XML的映射就无法将类型信息传递给框架了。如果java type没有设置而且框架无法获知类型的话,那么这个类型会被指定为Object。

(四) nullValue

属性 nullValue的值可以是对于property 类型来说合法的任意值,用于指定NULL 的替换值。就是说,当Java Bean的属性值等于指定值时,相应的字段将赋值NULL。这个特性允许在应用中给不支持null的数据类型(即int,double,float等)赋值null。当这些数据类型的属性值匹配nullValue值(即匹配-9999)时,NULL 将代替nullValue 值写入数据库。

例如:

<parameterMap id="authorParameter" class="Author">

         <parameter property="name" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

         <parameter property="age" jdbcType="INTEGER" javaType="java.lang.Integer" nullValue="-999" mode="INOUT"/>

         <parameter property="telephone" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

        <parameter property="address" jdbcType="VARCHAR" javaType="java.lang.String" nullValue="NO_ENTRY" mode="INOUT"/>

</parameterMap>

<insert id="insertAuthor1" parameterMap="authorParameter">

         INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)

</insert>

您可以在另一个SQL Map XML 文件中引用parameterMap。例如,要在另一个文件中引用上面的parameterMap,可以使用名称"Product.insert-product-param"。

(五) Inline Parameter Maps

使用Inline Parameter Maps,可以把Java Bean 的属性名称嵌在mapped-statement 的定义中(即直接写在SQL 语句中)。

例如:

<insert id="insertAuthor1" parameterClass="Author">

         INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)

</insert>

这样,在你的Author类中,要有name,age,telephone,address的属性以及相应的get和set方法,这样做可以避免使用另外定义parameterMap的麻烦。

你也可以在内嵌参数中指定数据类型和nullValue,例如:

<insert id="insertAuthor1" parameterClass="Author">

        INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name:VARCHAR:NO_ENTRY#,#age:INTEGER:-999#,#telephone:VARCHAR:NO_ENTRY#,#address:VARCHAR:NO_ENTRY#)

</insert>

注意!在内嵌参数中,要指定NULL 的替代值,必须要先指定数据类型。

注意!如需要在查询时也使用NULL 替代值,必须同时在resultMap 中定义。

注意!如果您需要指定很多的数据类型和NULL 替代值,可以使用外部的parameterMap元素,这样会使代码更清晰。

六、Result Maps

在SQL Map 架构中,Result Map 是极其重要的组件。在执行查询Mapped Statement 时,resultMap 负责将结果集的列值映射成Java Bean 的属性值。resultMap 的结构如下:

<resultMap id="resultMapName" class="some.domain.Class" [extends="parent-resultMap"]>

         <result property="propertyName" column="COLUMN_NAME"

                            [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"]

                            [nullValue="-999999"] [select="someOtherStatement"]

                            />

         <result ……/>

         <result ……/>

         <result ……/>

</resultMap>

括号[]中是可选的属性resultMap 也有class 属性,是Java 类的全限定名(即包括包的名称)或该类的别名。该Java 类初始化并根据定义填充数据。

Extends 是可选的属性,可以设定成以为基础的另外一个resultMap 的名字。和在Java 中继承一个类相似,父resultMap 的属性将作为子resultMap 的一部分。父resultMap 的属性总是加到子resultMap 属性的前面,并且父resultMap 必须要在子resultMap 之前定义。父resultMap 和子resultMap 的class 属性不一定要一致,它们可以没有任何关系。

resultMap 可以包括任意多的property 映射,将查询结果集的列值映射成Java Bean 的属性。属性的映射按它们在resultMap中定义的顺序进行。属性class 必须符合Java Bean 规范,每一属性都必须拥有get/set 方法。

注意!ResultSet 的列值按它们在resultMap 中定义的顺序读取。

(一) property

property属性是指从mapped statement中返回的JavaBean对象的属性名。这个属性名也可以使用多次。

(二) column

column属性值是ResultSet中的列名字,即字段名,取得的这个字段的值将赋给property所指的bean属性。

(三) columnIndex

可选属性,用于改善性能。属性columnIndex 的值是ResultSet 中用于赋值Java Bean属性的字段次序号。在99%的应用中,不太可能需要牺牲可读性来换取性能。使用columnIndex,某些JDBC Driver可以大幅提高性能,某些则没有任何效果。

(四) jdbcType

同ParameterMap中的jdbcType

(五) javaType

同ParameterMap中的javaType

(六) nullValue

属性nullValue指定数据库中NULL的替代值。因此,如果从ResultSet中读出NULL值,JavaBean属性将被赋值为属性nullValue指定的替代值。

如果数据库中存在NULLABLE 属性的字段,但您想在你的应用程序中用指定的常量代替NULL,您可以这样做:

<resultMap id="get-product-result" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name" nullValue="you have no name"/>

</resultMap>

在上例中,如果取得的记录中auth_name字段的值为NULL,那么在赋给java bean的时候,name属性将被赋为"you have no name"。

(七) select 复杂属性

如果在一个类与另一个类之间是关联关系的话,那么当你用JDBC取得记录的时候,这个关联关系是如何实现的呢?例如这样的关系:一个作者可能会有多个文章发表,那么作者与文章之间就是很强的关联关系,而且是一对多的关系,在Author的Bean中是这样写的:

public class Author

{

    private int id;

.....

    private List articleList;

         public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

         ... ...

       public List getArticleList()

    {

        return articleList;

    }

   

    public void setArticleList(List articleList)

    {

        this.articleList=articleList;

    }

}

当你执行一条sql语句从数据表author中取出相应的数据的时候,在上面的java bean中,articleList如何赋值呢?这时候,就需要使用select属性。

1.1:1关系:

我们先假设在author和article之间使用1:1的关系,虽然在真实世界中是不正确的,我们只是做个例子,那么Author和article的bean代码如下:

public class Author

{

    private int id;

    private int age;

    private String name;  

    private String address;

    private String telephone;

    private Article article;

      

    public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age=age;

    }

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name=name;

    }

    public String getAddress()

    {

        return address;

    }

    public void setAddress(String address)

    {

        this.address=address;

    }

    public String getTelephone()

    {

        return telephone;

    }

    public void setTelephone(String telephone)

    {

        this.telephone=telephone;

    }

 

         public Article getArticle()

    {

        return this.article;

    }

   

    public void setArticle(Article article)

    {

        this.article=article;

    }

public class Article

{

    private int id;

    private String title;

    private Date createtime;

    private int author;

   

    public int getId()

    {

        return id;

    }

    public void setId(int id)

    {

        this.id=id;

    }

    public String getTitle()

    {

        return title;

    }

    public void setTitle(String title)

    {

        this.title=title;

    }

    public Date getCreatetime()

    {

        return createtime;

    }

    public void setCreatetime(Date createtime)

    {

        this.createtime=createtime;

    }

    public int getAuthor()

    {

        return author;

    }

    public void setAuthor(int author)

    {

        this.author=author;

    }

}

在author.xml的配置如下:

<resultMap id="linkResultMap1" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article" column="auth_id" select="getLinkArticle1"/>

</resultMap>

<select id="getAuthor5" resultMap="linkResultMap1" parameterClass="int">

         SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle1" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id#

</select>

调用代码如下:

Author author = (Author)sqlMapClient.queryForObject("getAuthor5", new Integer(1));

System.out.println(author.getName()+"'s article is :"+author.getArticle().getTitle());

你可以看到,对于Author类中的article属性,IBatis是将取得的记录的auth_id字段值作为参数传入到id="getLinkArticle1"的语句中,并将取得的结果封装成Article对象,并赋给Author的article属性。上面的调用代码实际执行的两条sql语句是:

SELECT * FROM author WHERE auth_id = 1

SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = 1

在第二条语句中,将取得的记录封装成Article对象,并赋给Author的article属性,所以,你可以直接使用author.getArticle().getTitle()获得文章的标题。

上面的方法显示了如何实现1:1的关联关系,但是上面的方法并不好,原因是可能会执行很多次查询!

(1)避免 N+1 Selects (1:1)

如果上面的配置如下:

<resultMap id="linkResultMap1" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article" column="auth_id" select="getLinkArticle1"/>

</resultMap>

<select id="getAuthor5" resultMap="linkResultMap1" parameterClass="int">

         SELECT * FROM author WHERE auth_id > #id#

</select>

<select id="getLinkArticle1" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id#

</select>

调用代码如下:

Author author = (Author)sqlMapClient.queryForList("getAuthor5", new Integer(1));

如果SELECT * FROM author WHERE auth_id > 1的记录有N条,那么将对id="getLinkArticle1"的语句执行N次查询,这样所有的查询总数将为N+1次,执行效率会很低。

这时,可以使用下面的联合查询的方法来解决:

<resultMap id="linkResultMap2" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="article.title" column="art_title"/>

</resultMap>

<select id="getAuthor6" resultMap="linkResultMap2" parameterClass="int">

         <![CDATA[ SELECT a.auth_id,a.auth_age,a.auth_name,a.auth_tel,a.auth_address,b.art_title FROM author a,article b WHERE a.auth_id > #id# and a.auth_id = b.art_id]]>

</select>

调用代码为:

Author author = (Author)sqlMapClient.queryForList("getAuthor6", new Integer(1));

这样只用一条Sql语句就可以解决。

2.1:M与M:N关系:

下面我们讨论1:M的关系,一个author可能有多个article,所以,author与article之间是一对多的关系。那么我们在Author类中加入如下代码(在省略号间的是要加入的代码):

public class Author

{

... ...

         private List articleList;

    public List getArticleList()

    {

        return articleList;

    }

   

    public void setArticleList(List articleList)

    {

        this.articleList=articleList;

    }

... ...

}

配置如下:

<resultMap id="linkResultMap3" class="Author">

         <result property="id" column="auth_id"/>    

         <result property="age" column="auth_age"/>

         <result property="name" column="auth_name"/>

         <result property="telephone" column="auth_tel"/>

         <result property="address" column="auth_address"/>

         <result property="articleList" column="auth_id" select="getLinkArticle3"/>

</resultMap>

<select id="getAuthor7" resultMap="linkResultMap3" parameterClass="int">

         SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle3" resultClass="com.ibatis.beans.Article" parameterClass="int">

         SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id#

</select>

调用代码为:

Author author = (Author)sqlMapClient.queryForObject("getAuthor7", new Integer(1));

System.out.println(author.getName()+"的文章有:");

for(int i=0;i<author.getArticleList().size();i++)

{

         int num=i+1;

         Article art = (Article)author.getArticleList().get(i);

         System.out.println(num+". "+art.getTitle());

}

从上面的实现可以看出,你只需要在bean中加入一个java.util.List(或java.util.Collection)类型的articleList来表示所有的文章列表即可,调用部分没有什么变化,IBaits会自动从Article中取得的记录封装成Article对象并加入到一个List对象中,然后将这个List对象赋值给Author类的articleList属性。

(1)避免 N+1 Selects (1:M and M:N)

1:M和M:N的情况与1:1的情况相似,也会出现N+1 Selects 的情况,但是到目前位置,还没有其他的方法来解决这个问题,希望在不久的将来能够解决。

3.多个复杂参数属性

你可能已经注意到了,上面的例子中,在resultMap中只指明了一个column属性用于id=”getLinkArticle”的statement关联。其实,Ibatis允许你指明多个column属性与id=”getLinkArticle”的statement关联,语法很简单, {param1=column1, param2=column2, …, paramN=columnN}。下面是一个例子:

<resultMap id="linkResultMap4" class="Author">

    <result property="id" column="auth_id"/>   

    <result property="age" column="auth_age"/>

    <result property="name" column="auth_name"/>

    <result property="telephone" column="auth_tel"/>

    <result property="address" column="auth_address"/>

    <result property="articleList" column="{id=auth_id,address=auth_address}" select="getLinkArticle4"/>

</resultMap>

<select id="getAuthor8" resultMap="linkResultMap4" parameterClass="int">

    SELECT * FROM author WHERE auth_id = #id#

</select>

<select id="getLinkArticle4" resultClass="com.ibatis.beans.Article" parameterClass="int">

    SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id# and art_publish_add=#address#

</select>

你也可以只写字段的名称,只要按照所关联的statement中所对应的字段顺序即可,象这样:

{auth_id,auth_address}

上面的这个例子我在Mysql的环境中运行没有通过,报出的错误是:Column'{auth_id,auth_address}' not found.

这个错误是与JDBC驱动无关的,而是在做XML解析的时候发生的错误,不知道是什么原因,如果您有这样的成功经历的话,希望能共同分享,我的Email是:tenwang1977@163.com

注意:有些JDBC驱动不支持同时打开多个ResultSets(单个连接)。所以说,这样的驱动不能完成复杂对象的映射,因为JDBC驱动需要多个ResultSets的连接,这时候,只能使用一个关联查询解决问题。

如果你使用Microsoft SQL Server 2000 的JDBC驱动的话,你需要在配置url的时候,在url后加上SelectMethod=Cursor

4.在Parameter Maps and Result Maps中支持的参数

Java Type

JavaBean/Map

Property Mapping

Result Class /

Parameter Class***

Type Alias**

boolean

YES

NO

boolean

java.lang.Boolean

YES

YES

boolean

byte

YES

NO

byte

java.lang.Byte

YES

YES

byte

short

YES

NO

short

java.lang.Short

YES

YES

short

int

YES

NO

Int/ Integer

java.lang.Integer

YES

YES

Int/ Integer

long

YES

NO

long

java.lang.Long

YES

YES

long

float

YES

NO

float

java.lang.Float

YES

YES

float

double

YES

NO

double

java.lang.Double

YES

YES

double

java.lang.String

YES

YES

string

java.util.Date

YES

YES

date

java.math.BigDecimal

YES

YES

decimal

* java.sql.Date

YES

YES

N/A

* java.sql.Time

YES

YES

N/A

* java.sql.Timestamp

YES

YES

N/A

七、缓存Mapped Statement Result

<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>

上面的cache model 创建了一个名为“product-cache”的缓存,使用“最近最少使用”(LRU)实现,每24小时,缓冲区将刷新一次,而且在执行insertProduct、updateProduct和deleteProduct的statement时,缓冲区也将刷新,设定的时间可以设定为hours,minutes, seconds或 milliseconds。一些Cache的实现需要附加的属性,比如说上例中的cache-size

 属性,cache的大小指明了可以存放在cache中的实体的个数。type属性的名称要么是全限定的类名,要么是缓存实现的别名。Cache Model 使用插件的形式来支持不同的缓存算法。它的实现在cache-model 元素的type属性中指定(如上所示)。

(一)Read-Only 与 Read/Write

Ibatis支持只读和可读写的Cache,只读的Cache可以在所有的用户间共享,所以它可以提供更大的操作空间。但是从只读缓冲中读取的对象不能够被修改。如果你要对你取得的对象进行修改的话,那么你只能用可读写的缓冲。readOnly=”true”为只读缓冲;readOnly=”false”为可读写缓冲。

(二)Serializable Read/Write Caches

要使用Serializable Read/Write Caches,设置readOnly=”false”serialize=”true”。默认情况下,采用的是readOnly=” true”serialize=”false”。

(三)缓冲类型

目前包括以下的4 个缓冲类型实现:

1. “MEMORY” (com.ibatis.db.sqlmap.cache.memory.MemoryCacheController)

MEMORY cache 实现使用reference 类型来管理cache 的行为。垃圾收集器可以根据reference 类型判断是否要回收cache 中的数据。MEMORY 实现适用于没有统一的对象重用模式的应用,或内存不足的应用。

MEMORY 实现可以这样配置:

<cache-model name="product-cache" implementation ="MEMORY">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”reference-type” value=”WEAK” />

</cache-model>

MEMORY cache 实现只认识一个<cache-property>元素。这个名为“reference-type”属性的值必须是STRONG,SOFT 和WEAK 三者其一。这三个值分别对应于JVM 不同的内存reference 类型。

(1) WEAK(缺省)

大多数情况下,WEAK类型是最佳选择。如果不指定类型,缺省类型就是WEAK。它能大大提高常用查询的性能。但是对于当前不被使用的查询结果数据,将被清除以释放内存用来分配其他对象。

(2) SOFT

在查询结果对象数据不被使用,同时需要内存分配其他对象的情况下,SOFT类型将减少内存不足的可能性。然而,这不是最具侵入性的reference类型,结果数据依然可能被清除。

(3) STRONG

确保查询结果数据一直保留在内存中,除非Cache被刷新(例如,到了刷新的时间或执行了更新数据的操作)。

对于下面的情况,这是理想的选择:

1” 结果内容数据很少

2” 完全静态的数据

3” 频繁使用的数据

优点是对于这类查询性能非常好。缺点是,如果需要分配其他对象,内存无法释放(可能是更重要的数据对象)。

2. “LRU” (com.ibatis.db.sqlmap.cache.lru.LruCacheController)

LRU Cache 实现用“最近最少使用”原则来确定如何从Cache 中清除对象。当Cache溢出时,最近最少使用的对象将被从Cache 中清除。

<cache-model name="product-cache" implementation ="LRU">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”cache-size” value=”1000” />

</cache-model>

值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。

3. “FIFO” (com.ibatis.db.sqlmap.cache.fifo.FifoCacheController)

FIFO Cache 实现用“先进先出”原则来确定如何从Cache 中清除对象。对于短时间内持续引用特定的查询而后很可能不再使用的情况,FIFO Cache 是很好的选择。

<cache-model name="product-cache" implementation ="FIFO">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

         <cache-property name=”cache-size” value=”1000” />

</cache-model>

值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。

4. “OSCACHE” (com.ibatis.db.sqlmap.cache.oscache.OSCacheController)

OSCACHE Cache 实现是OSCache2.0 缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。

<cache-model name="product-cache" implementation ="OSCACHE">

         <flush-interval hours="24"/>

         <flush-on-execute statement="insertProduct"/>

         <flush-on-execute statement="updateProduct"/>

         <flush-on-execute statement="deleteProduct"/>

</cache-model>

OSCACHE 实现不使用cache-property 元素。而是在类路径的根路径中使用标准的oscache.properties 文件进行配置。在oscache.properties 文件中,您可以配置Cache 的算法(和上面讨论的算法很类似),Cache 的大小,持久化方法(内存,文件等)和集群方法。

原创粉丝点击