http://www.cnblogs.com/dwjaissk/archive/2006/03/07/344999.html
例如:
(二)
例如:
(二)
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)
(三)
例如:
下面是在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
(四)
注意:在Sql Map 1.0版本中,允许同时配置多个数据源,这样会导致一些问题。所以从2.0的版本开始,只允许配置一个数据源,如果你有配置多个数据源的需求的话,建议你使用多个不同配置的属性文件,或者在build Sql Map的时候,作为一个参数传进来。
在框架中已经包含了三种事务管理:JDBC, JTA 和 EXTERNAL
1. JDBC
允许用JDBC通过Connection 的commit()和rollback()方法来控制事务。
2. JTA
这种事务管理使用一个JTA的全局事务将SQL Map的activities作为一个wider scope事务的一部分而包含进来,这个事务可能包含其他的数据库或事务
源。这个配置需要一个UserTransaction属性来通过JNDI resource设置本地的user transaction。
3. EXTERNAL
允许你自己管理事务。你仍然可以配置一个数据源,但是事务不会在框架生命周期中被提交或回滚。这意味着你必须要用自己的程序来控制事务。这个设置对于非事务型数据库是非常有用的。
(五)
目前本框架提供三种数据源工厂,但是你也可以自己写一个。下面是每一种数据源工厂的配置举例:
1. SimpleDataSourceFactory
SimpleDataSourceFactory 为池化的DataSource提供了一个基本的实现,适用于在没有容器提供数据源的情况。
2. DbcpDataSourceFactory
DbcpDataSourceFactory 实现使用Jakarta DBCP(Database Connection Pool)的DataSource
API 提供连接池服务。适用于Web 容器不提供DataSource 服务的情况,或执行一个单独的
应用。DbcpDataSourceFactory 中必须要配置的参数例子如下:
3. JndiDataSourceFactory
JndiDataSourceFactory 在应用服务器的容器中从JNDI Context 中查找DataSource 实现。当使用应用
服务器,并且服务器提供了容器管理的连接池和相关的DataSource 实现的情况下,可以使用
JndiDataSourceFactory。使用JDBC DataSource 的标准方法是通过JNDI 来查找。
JndiDataSourceFactory必须要配置的属性如下:
注意:上面的配置是使用标准的JDBC事务管理。但是,在一个容器管理的数据源中,你也可能想为全局的事务做如下配置:
注意:UserTransaction属性指向一个JNDI的位置,你可以通过这个JNDI找到一个UserTransaction实例。这个在JTA的事务管理中是需要的,这样可以使你的SQL MAP参与到包含有其他数据库和事务源的事务中。
(六)
sqlMap元素用于包括SQL Map 映射文件和其他的SQL Map 配置文件。每个SqlMapClient
对象使用的SQL Map 映射文件都要在此声明。映射文件作为stream resource 从类路径或URL中读
入。您必须相对于类路径或URL来指定所有的SQL Map 文件。
下面是几个例子:
sqlMap所包含的标签:
select * from PRODUCT where PRD_ID = ?
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在内存中缓冲经常使用的数据。
[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的配置中,放在[]中的配置参数是可选的,所以下面的Mapped Statement是完全合法的。
insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, "Shih Tzu")
下面逐一介绍各个标签的含义以及使用方法:
1. Statement 类型
下表中列出了所有的Statement,以及他们的属性和所支持的特征。
Statement Element
|
Attributes
|
Child Elements
|
Methods
|
|
id parameterClass resultClass parameterMap resultMap cacheModel xmlResultName |
All dynamic elements |
insert update delete All query methods |
|
id parameterClass parameterMap |
All dynamic elements |
insert update delete |
|
id parameterClass parameterMap |
All dynamic elements
|
insert update delete |
|
id parameterClass parameterMap |
All dynamic elements |
insert update delete |
|
id parameterClass resultClass parameterMap resultMap cacheModel |
All dynamic elements |
All query methods |
|
id parameterClass resultClass parameterMap resultMap xmlResultName |
All dynamic elements |
insert update delete All query methods |
[parameterClass="some.class.Name"] [resultClass="some.class.Name"] [parameterMap="nameOfParameterMap"] [resultMap="nameOfResultMap"] [cacheModel="nameOfCache"]> select * from PRODUCT where PRD_ID = [?|#propertyName#] order by [$simpleDynamic$]
2. the SQL
sql语句无疑是map中最重要的组成部分,你可以使用任何的sql语句,只要你的数据库和JDBC驱动支持就行。你也可以使用数据库和驱动支持的任何函数。因为你是将sql语句写在了XML文档中,为了区别Sql中的"<>"和XML中的"<>",在写Sql时,可以用来写。
3. 自增
很多关系型数据库都支持主键的自增,SQL Map可以通过
SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)
insert into PRODUCT (PRD_DESCRIPTION) values (#description#)
SELECT @@IDENTITY AS ID
4. 存储过程:
通过
{call swap_email_address (?, ?)}
调用上面的存储过程会在数据表的两列间交换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 INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)
在上面的语句中,你指定的parameterClass=Author,那么在你的Author类中要有name,age,telephone和address属性,并且要有相应的get和set方法
例2:
你可以使用基本类型作为parameterClass,如:
delete from author WHERE auth_id = #id#
例3:
你可以使用HashMap作为parameterClass,如:
INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#)
这时候,在你调用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,将在后文中介绍。
例如:
INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)
上面的例子中,parameterMap的参数按次序匹配SQL语句中的值符号(?)。因此,第一个"?"号将被"name"属性的值替换,而第二个"?"号将被"age"属性的值替换,依此类推。
记住:使用parameterMap的时候,SQL中的参数用"?"来代替,并且每个"?"的顺序要与parameterMap中的定义完全匹配;如果使用parameterClass,那么SQL中的参数用"#parameterName#"来代替,如果传入的参数类为Bean,那么要有get和set这个参数名的方法。
3. resultClass
resultClass 属性可以让您指定一个Java 类,根据ResultSetMetaData 将其自动映射到JDBC ResultSet。只要是JavaBean 的属性、方法名称和ResultSet的列名匹配,属性自动赋值列值。
例1:
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#
在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。
如果你写成:
SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_id = #id#
那么在你的Author类中,要有auth_id,auth_name,auth_age,auth_tel,auth_address属性,并且要有相应的get和set方法。
例2:
你还可以使用基本类型作为resultClass,如:
例3:
你还可以使用HashMap作为resultClass,如:
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
下面是调用代码:
List authorList = (List)sqlMapClient.queryForList("getAuthor4",null);
showMethod("getAllAuthor");
for(int i=0;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 的替代值。 例如: SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 或 SELECT * FROM author 在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。 通过resultMap 的定义,查询语句得到的ResultSet 被映射成Author对象。resultMap定义"id"属性值将赋予"auth_id"字段值,而"telephone"属性值将赋予"auth_tel"字段值,依次类推。 注意:在resultMap中所指定的字段必须是下面的select中的子集。 也就是说,你不能写成 SELECT auth_address FROM author WHERE auth_address like #%address%# 但是你可以在resultMap中去掉 SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 这样的话,你就无法取得auth_id的值。 5. cacheModel 定义查询mapped statement 的缓存。每一个查询mapped statement 可以使用不同或相同的cacheModel。 SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 上面的配置说明:"getAuthor3"的缓存使用WEAK引用类型,当你通过调用"getAuthor3"的时候,Ibatis将会把结果缓存起来。每24 小时缓存刷新一次,或当更新的操作(即上面配置的insertProduct、updateProduct或deleteProduct)发生时刷新。 当你对某些表中的记录操作频繁时,可以考虑使用缓冲,但是如果数据量过大的话,最好另想办法。 6. xmlResultName 当映射结果指向一个XML文档的时候,xmlResultName的值是指那个XML文档的root标签的名字。例如: 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将产生如下的XML对象:
[nullValue="NUMERIC"] [null="-9999999"]/> 括号[]中是可选的属性。parameterMap 元素的id 属性作为唯一标识,在同一个SQL Map XML 文件中不能重名。一个parameterMap 可包含任意多的property 元素。 property属性是指传入mapped statement中的JavaBean参数对象的属性名。这个属性名可以使用多次,这要看在这个statement中,这个属性名要出现多少次。例如: UPDATE author set auth_name=? WHERE auth_name = ? 但是如果使用这样的方法的话,调用代码应该是: 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 = '作者三' 方法倒是有,不过我觉得不太好。 UPDATE author set auth_name=? WHERE auth_name = ? 调用代码为: HashMap paraMap = new HashMap(); paraMap.put("name1", "作者N"); paraMap.put("name2", "作者三"); sqlMapClient.update("updateAuthor2", paraMap); 如果你想到更好的方法解决这个问题的话,请不吝赐教。 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用于指明作为参数传递的java bean的属性的类型。通常情况下,这可以通过反射机制从java bean中获取类型,但是一些特定的映射,比如说MAP和XML的映射就无法将类型信息传递给框架了。如果java type没有设置而且框架无法获知类型的话,那么这个类型会被指定为Object。 属性 nullValue的值可以是对于property 类型来说合法的任意值,用于指定NULL 的替换值。就是说,当Java Bean的属性值等于指定值时,相应的字段将赋值NULL。这个特性允许在应用中给不支持null的数据类型(即int,double,float等)赋值null。当这些数据类型的属性值匹配nullValue值(即匹配-9999)时,NULL 将代替nullValue 值写入数据库。 例如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?) 您可以在另一个SQL Map XML 文件中引用parameterMap。例如,要在另一个文件中引用上面的parameterMap,可以使用名称"Product.insert-product-param"。 使用Inline Parameter Maps,可以把Java Bean 的属性名称嵌在mapped-statement 的定义中(即直接写在SQL 语句中)。 例如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#) 这样,在你的Author类中,要有name,age,telephone,address的属性以及相应的get和set方法,这样做可以避免使用另外定义parameterMap的麻烦。 你也可以在内嵌参数中指定数据类型和nullValue,例如: 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#) 注意!在内嵌参数中,要指定NULL 的替代值,必须要先指定数据类型。 注意!如需要在查询时也使用NULL 替代值,必须同时在resultMap 中定义。 注意!如果您需要指定很多的数据类型和NULL 替代值,可以使用外部的parameterMap元素,这样会使代码更清晰。 在SQL Map 架构中,Result Map 是极其重要的组件。在执行查询Mapped Statement 时,resultMap 负责将结果集的列值映射成Java Bean 的属性值。resultMap 的结构如下: [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"] [nullValue="-999999"] [select="someOtherStatement"] /> 括号[]中是可选的属性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属性是指从mapped statement中返回的JavaBean对象的属性名。这个属性名也可以使用多次。 column属性值是ResultSet中的列名字,即字段名,取得的这个字段的值将赋给property所指的bean属性。 可选属性,用于改善性能。属性columnIndex 的值是ResultSet 中用于赋值Java Bean属性的字段次序号。在99%的应用中,不太可能需要牺牲可读性来换取性能。使用columnIndex,某些JDBC Driver可以大幅提高性能,某些则没有任何效果。 同ParameterMap中的jdbcType 同ParameterMap中的javaType 属性nullValue指定数据库中NULL的替代值。因此,如果从ResultSet中读出NULL值,JavaBean属性将被赋值为属性nullValue指定的替代值。 如果数据库中存在NULLABLE 属性的字段,但您想在你的应用程序中用指定的常量代替NULL,您可以这样做: 在上例中,如果取得的记录中auth_name字段的值为NULL,那么在赋给java bean的时候,name属性将被赋为"you have no name"。 如果在一个类与另一个类之间是关联关系的话,那么当你用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属性。 我们先假设在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的配置如下: SELECT * FROM author WHERE auth_id = #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id# 调用代码如下: 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) 如果上面的配置如下: SELECT * FROM author WHERE auth_id > #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id# 调用代码如下: Author author = (Author)sqlMapClient.queryForList("getAuthor5", new Integer(1)); 如果SELECT * FROM author WHERE auth_id > 1的记录有N条,那么将对id="getLinkArticle1"的语句执行N次查询,这样所有的查询总数将为N+1次,执行效率会很低。 这时,可以使用下面的联合查询的方法来解决: #id# and a.auth_id = b.art_id]]> 调用代码为: Author author = (Author)sqlMapClient.queryForList("getAuthor6", new Integer(1)); 这样只用一条Sql语句就可以解决。 下面我们讨论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; } ... ... } 配置如下: SELECT * FROM author WHERE auth_id = #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id# 调用代码为: Author author = (Author)sqlMapClient.queryForObject("getAuthor7", new Integer(1)); System.out.println(author.getName()+"的文章有:"); for(int i=0;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 的情况,但是到目前位置,还没有其他的方法来解决这个问题,希望在不久的将来能够解决。 你可能已经注意到了,上面的例子中,在resultMap中只指明了一个column属性用于id=”getLinkArticle”的statement关联。其实,Ibatis允许你指明多个column属性与id=”getLinkArticle”的statement关联,语法很简单, {param1=column1, param2=column2, …, paramN=columnN}。下面是一个例子: SELECT * FROM author WHERE auth_id = #id# 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# 你也可以只写字段的名称,只要按照所关联的statement中所对应的字段顺序即可,象这样: {auth_id,auth_address} 上面的这个例子我在Mysql的环境中运行没有通过,报出的错误是:Column'{auth_id,auth_address}' not found. 这个错误是与JDBC驱动无关的,而是在做XML解析的时候发生的错误,不知道是什么原因,如果您有这样的成功经历的话,希望能共同分享,我的Email是:[email protected] 注意:有些JDBC驱动不支持同时打开多个ResultSets(单个连接)。所以说,这样的驱动不能完成复杂对象的映射,因为JDBC驱动需要多个ResultSets的连接,这时候,只能使用一个关联查询解决问题。 如果你使用Microsoft SQL Server 2000 的JDBC驱动的话,你需要在配置url的时候,在url后加上SelectMethod=Cursor。 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 上面的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属性中指定(如上所示)。 Ibatis支持只读和可读写的Cache,只读的Cache可以在所有的用户间共享,所以它可以提供更大的操作空间。但是从只读缓冲中读取的对象不能够被修改。如果你要对你取得的对象进行修改的话,那么你只能用可读写的缓冲。readOnly=”true”为只读缓冲;readOnly=”false”为可读写缓冲。 要使用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 实现可以这样配置: MEMORY cache 实现只认识一个 (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 中清除。 值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。 3. “FIFO” (com.ibatis.db.sqlmap.cache.fifo.FifoCacheController) FIFO Cache 实现用“先进先出”原则来确定如何从Cache 中清除对象。对于短时间内持续引用特定的查询而后很可能不再使用的情况,FIFO Cache 是很好的选择。 值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。 4. “OSCACHE” (com.ibatis.db.sqlmap.cache.oscache.OSCacheController) OSCACHE Cache 实现是OSCache2.0 缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。 OSCACHE 实现不使用cache-property 元素。而是在类路径的根路径中使用标准的oscache.properties 文件进行配置。在oscache.properties 文件中,您可以配置Cache 的算法(和上面讨论的算法很类似),Cache 的大小,持久化方法(内存,文件等)和集群方法。 元素 1. maxExecute 一般情况下,这个值要大于10,而且要同时比maxSessions和maxTransactions参数设定的值大。通常情况下,减少最大同时访问次数可以提高执行的效率。 2. maxSessions 3. maxTransaction 4. cacheModelsEnabled 5. lazyLoadingEnabled 6. enhancementEnabled 7. useStatementNamespaces 例子: useStatementNamespaces="false" (三) 2. Data Source Factory Aliases (四) 在框架中已经包含了三种事务管理:JDBC, JTA 和 EXTERNAL 2. JTA 3. EXTERNAL (五) 1. SimpleDataSourceFactory 2. DbcpDataSourceFactory 3. JndiDataSourceFactory JndiDataSourceFactory必须要配置的属性如下: 注意:UserTransaction属性指向一个JNDI的位置,你可以通过这个JNDI找到一个UserTransaction实例。这个在JTA的事务管理中是需要的,这样可以使你的SQL MAP参与到包含有其他数据库和事务源的事务中。 (六) sqlMap所包含的标签: select * from PRODUCT where PRD_ID = ? 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在内存中缓冲经常使用的数据。 [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的配置中,放在[]中的配置参数是可选的,所以下面的Mapped Statement是完全合法的。 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, "Shih Tzu") 下面逐一介绍各个标签的含义以及使用方法: 1. Statement 类型 下表中列出了所有的Statement,以及他们的属性和所支持的特征。 Statement Element Attributes Child Elements Methods id parameterClass resultClass parameterMap resultMap cacheModel xmlResultName All dynamic elements insert update delete All query methods id parameterClass parameterMap All dynamic elements insert update delete id parameterClass parameterMap All dynamic elements insert update delete id parameterClass parameterMap All dynamic elements insert update delete id parameterClass resultClass parameterMap resultMap cacheModel All dynamic elements All query methods id parameterClass resultClass parameterMap resultMap xmlResultName All dynamic elements insert update delete All query methods [parameterClass="some.class.Name"] [resultClass="some.class.Name"] [parameterMap="nameOfParameterMap"] [resultMap="nameOfResultMap"] [cacheModel="nameOfCache"]> select * from PRODUCT where PRD_ID = [?|#propertyName#] order by [$simpleDynamic$] 2. the SQL sql语句无疑是map中最重要的组成部分,你可以使用任何的sql语句,只要你的数据库和JDBC驱动支持就行。你也可以使用数据库和驱动支持的任何函数。因为你是将sql语句写在了XML文档中,为了区别Sql中的"<>"和XML中的"<>",在写Sql时,可以用来写。 3. 自增 很多关系型数据库都支持主键的自增,SQL Map可以通过 SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#) insert into PRODUCT (PRD_DESCRIPTION) values (#description#) SELECT @@IDENTITY AS ID 4. 存储过程: 通过 {call swap_email_address (?, ?)} 调用上面的存储过程会在数据表的两列间交换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 INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#) 在上面的语句中,你指定的parameterClass=Author,那么在你的Author类中要有name,age,telephone和address属性,并且要有相应的get和set方法 例2: 你可以使用基本类型作为parameterClass,如: delete from author WHERE auth_id = #id# 例3: 你可以使用HashMap作为parameterClass,如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#) 这时候,在你调用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,将在后文中介绍。 例如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?) 上面的例子中,parameterMap的参数按次序匹配SQL语句中的值符号(?)。因此,第一个"?"号将被"name"属性的值替换,而第二个"?"号将被"age"属性的值替换,依此类推。 记住:使用parameterMap的时候,SQL中的参数用"?"来代替,并且每个"?"的顺序要与parameterMap中的定义完全匹配;如果使用parameterClass,那么SQL中的参数用"#parameterName#"来代替,如果传入的参数类为Bean,那么要有get和set这个参数名的方法。 3. resultClass resultClass 属性可以让您指定一个Java 类,根据ResultSetMetaData 将其自动映射到JDBC ResultSet。只要是JavaBean 的属性、方法名称和ResultSet的列名匹配,属性自动赋值列值。 例1: 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# 在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。 如果你写成: SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_id = #id# 那么在你的Author类中,要有auth_id,auth_name,auth_age,auth_tel,auth_address属性,并且要有相应的get和set方法。 例2: 你还可以使用基本类型作为resultClass,如: 例3: 你还可以使用HashMap作为resultClass,如: 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 下面是调用代码: List authorList = (List)sqlMapClient.queryForList("getAuthor4",null); showMethod("getAllAuthor"); for(int i=0;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 的替代值。 例如: SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 或 SELECT * FROM author 在上面的语句中,你指定的resultClass=Author,那么在你的Author类中要有id,name,age,telephone和address属性,并且要有相应的get和set方法。 通过resultMap 的定义,查询语句得到的ResultSet 被映射成Author对象。resultMap定义"id"属性值将赋予"auth_id"字段值,而"telephone"属性值将赋予"auth_tel"字段值,依次类推。 注意:在resultMap中所指定的字段必须是下面的select中的子集。 也就是说,你不能写成 SELECT auth_address FROM author WHERE auth_address like #%address%# 但是你可以在resultMap中去掉 SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 这样的话,你就无法取得auth_id的值。 5. cacheModel 定义查询mapped statement 的缓存。每一个查询mapped statement 可以使用不同或相同的cacheModel。 SELECT auth_id,auth_name,auth_age,auth_tel,auth_address FROM author WHERE auth_address like #%address%# 上面的配置说明:"getAuthor3"的缓存使用WEAK引用类型,当你通过调用"getAuthor3"的时候,Ibatis将会把结果缓存起来。每24 小时缓存刷新一次,或当更新的操作(即上面配置的insertProduct、updateProduct或deleteProduct)发生时刷新。 当你对某些表中的记录操作频繁时,可以考虑使用缓冲,但是如果数据量过大的话,最好另想办法。 6. xmlResultName 当映射结果指向一个XML文档的时候,xmlResultName的值是指那个XML文档的root标签的名字。例如: 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将产生如下的XML对象: [nullValue="NUMERIC"] [null="-9999999"]/> 括号[]中是可选的属性。parameterMap 元素的id 属性作为唯一标识,在同一个SQL Map XML 文件中不能重名。一个parameterMap 可包含任意多的property 元素。 property属性是指传入mapped statement中的JavaBean参数对象的属性名。这个属性名可以使用多次,这要看在这个statement中,这个属性名要出现多少次。例如: UPDATE author set auth_name=? WHERE auth_name = ? 但是如果使用这样的方法的话,调用代码应该是: 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 = '作者三' 方法倒是有,不过我觉得不太好。 UPDATE author set auth_name=? WHERE auth_name = ? 调用代码为: HashMap paraMap = new HashMap(); paraMap.put("name1", "作者N"); paraMap.put("name2", "作者三"); sqlMapClient.update("updateAuthor2", paraMap); 如果你想到更好的方法解决这个问题的话,请不吝赐教。 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用于指明作为参数传递的java bean的属性的类型。通常情况下,这可以通过反射机制从java bean中获取类型,但是一些特定的映射,比如说MAP和XML的映射就无法将类型信息传递给框架了。如果java type没有设置而且框架无法获知类型的话,那么这个类型会被指定为Object。 属性 nullValue的值可以是对于property 类型来说合法的任意值,用于指定NULL 的替换值。就是说,当Java Bean的属性值等于指定值时,相应的字段将赋值NULL。这个特性允许在应用中给不支持null的数据类型(即int,double,float等)赋值null。当这些数据类型的属性值匹配nullValue值(即匹配-9999)时,NULL 将代替nullValue 值写入数据库。 例如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?) 您可以在另一个SQL Map XML 文件中引用parameterMap。例如,要在另一个文件中引用上面的parameterMap,可以使用名称"Product.insert-product-param"。 使用Inline Parameter Maps,可以把Java Bean 的属性名称嵌在mapped-statement 的定义中(即直接写在SQL 语句中)。 例如: INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (#name#,#age#,#telephone#,#address#) 这样,在你的Author类中,要有name,age,telephone,address的属性以及相应的get和set方法,这样做可以避免使用另外定义parameterMap的麻烦。 你也可以在内嵌参数中指定数据类型和nullValue,例如: 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#) 注意!在内嵌参数中,要指定NULL 的替代值,必须要先指定数据类型。 注意!如需要在查询时也使用NULL 替代值,必须同时在resultMap 中定义。 注意!如果您需要指定很多的数据类型和NULL 替代值,可以使用外部的parameterMap元素,这样会使代码更清晰。 在SQL Map 架构中,Result Map 是极其重要的组件。在执行查询Mapped Statement 时,resultMap 负责将结果集的列值映射成Java Bean 的属性值。resultMap 的结构如下: [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"] [nullValue="-999999"] [select="someOtherStatement"] /> 括号[]中是可选的属性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属性是指从mapped statement中返回的JavaBean对象的属性名。这个属性名也可以使用多次。 column属性值是ResultSet中的列名字,即字段名,取得的这个字段的值将赋给property所指的bean属性。 可选属性,用于改善性能。属性columnIndex 的值是ResultSet 中用于赋值Java Bean属性的字段次序号。在99%的应用中,不太可能需要牺牲可读性来换取性能。使用columnIndex,某些JDBC Driver可以大幅提高性能,某些则没有任何效果。 同ParameterMap中的jdbcType 同ParameterMap中的javaType 属性nullValue指定数据库中NULL的替代值。因此,如果从ResultSet中读出NULL值,JavaBean属性将被赋值为属性nullValue指定的替代值。 如果数据库中存在NULLABLE 属性的字段,但您想在你的应用程序中用指定的常量代替NULL,您可以这样做: 在上例中,如果取得的记录中auth_name字段的值为NULL,那么在赋给java bean的时候,name属性将被赋为"you have no name"。 如果在一个类与另一个类之间是关联关系的话,那么当你用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属性。 我们先假设在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的配置如下: SELECT * FROM author WHERE auth_id = #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id# 调用代码如下: 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) 如果上面的配置如下: SELECT * FROM author WHERE auth_id > #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_id = #id# 调用代码如下: Author author = (Author)sqlMapClient.queryForList("getAuthor5", new Integer(1)); 如果SELECT * FROM author WHERE auth_id > 1的记录有N条,那么将对id="getLinkArticle1"的语句执行N次查询,这样所有的查询总数将为N+1次,执行效率会很低。 这时,可以使用下面的联合查询的方法来解决: #id# and a.auth_id = b.art_id]]> 调用代码为: Author author = (Author)sqlMapClient.queryForList("getAuthor6", new Integer(1)); 这样只用一条Sql语句就可以解决。 下面我们讨论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; } ... ... } 配置如下: SELECT * FROM author WHERE auth_id = #id# SELECT art_id as id,art_title as title,art_createtime as createtime,art_author as author FROM article WHERE art_author = #id# 调用代码为: Author author = (Author)sqlMapClient.queryForObject("getAuthor7", new Integer(1)); System.out.println(author.getName()+"的文章有:"); for(int i=0;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 的情况,但是到目前位置,还没有其他的方法来解决这个问题,希望在不久的将来能够解决。 你可能已经注意到了,上面的例子中,在resultMap中只指明了一个column属性用于id=”getLinkArticle”的statement关联。其实,Ibatis允许你指明多个column属性与id=”getLinkArticle”的statement关联,语法很简单, {param1=column1, param2=column2, …, paramN=columnN}。下面是一个例子: SELECT * FROM author WHERE auth_id = #id# 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# 你也可以只写字段的名称,只要按照所关联的statement中所对应的字段顺序即可,象这样: {auth_id,auth_address} 上面的这个例子我在Mysql的环境中运行没有通过,报出的错误是:Column'{auth_id,auth_address}' not found. 这个错误是与JDBC驱动无关的,而是在做XML解析的时候发生的错误,不知道是什么原因,如果您有这样的成功经历的话,希望能共同分享,我的Email是:[email protected] 注意:有些JDBC驱动不支持同时打开多个ResultSets(单个连接)。所以说,这样的驱动不能完成复杂对象的映射,因为JDBC驱动需要多个ResultSets的连接,这时候,只能使用一个关联查询解决问题。 如果你使用Microsoft SQL Server 2000 的JDBC驱动的话,你需要在配置url的时候,在url后加上SelectMethod=Cursor。 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 上面的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属性中指定(如上所示)。 Ibatis支持只读和可读写的Cache,只读的Cache可以在所有的用户间共享,所以它可以提供更大的操作空间。但是从只读缓冲中读取的对象不能够被修改。如果你要对你取得的对象进行修改的话,那么你只能用可读写的缓冲。readOnly=”true”为只读缓冲;readOnly=”false”为可读写缓冲。 要使用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 实现可以这样配置: MEMORY cache 实现只认识一个 (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 中清除。 值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。 3. “FIFO” (com.ibatis.db.sqlmap.cache.fifo.FifoCacheController) FIFO Cache 实现用“先进先出”原则来确定如何从Cache 中清除对象。对于短时间内持续引用特定的查询而后很可能不再使用的情况,FIFO Cache 是很好的选择。 值得注意的是,这里指的对象可以是任意的,从单一的String 对象到Java Bean 的ArrayList 对象都可以。因此,不要Cache 太多的对象,以免内存不足。 4. “OSCACHE” (com.ibatis.db.sqlmap.cache.oscache.OSCacheController) OSCACHE Cache 实现是OSCache2.0 缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。 OSCACHE 实现不使用cache-property 元素。而是在类路径的根路径中使用标准的oscache.properties 文件进行配置。在oscache.properties 文件中,您可以配置Cache 的算法(和上面讨论的算法很类似),Cache 的大小,持久化方法(内存,文件等)和集群方法。(一) property
(二) jdbcType
(三) javaType
(四) nullValue
(五) Inline Parameter Maps
六、Result Maps
(一) property
(二) column
(三) columnIndex
(四) jdbcType
(五) javaType
(六) nullValue
(七) select 复杂属性
1.1:1关系:
2.1:M与M:N关系:
3.多个复杂参数属性
4.在Parameter Maps and Result Maps中支持的参数
七、缓存Mapped Statement Result
(一)Read-Only 与 Read/Write
(二)Serializable Read/Write Caches
(三)缓冲类型
同时执行一个Sql statement的最大线程数,大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。
例子:maxExecute="256"
缺省值:512
是指在一个给定时间内处于活动状态的session(或客户端)的数量。这个值一般要大于或等于maxTransactions的参数值,同时要小于maxRequests的参数值
例子: maxSessions="64"
缺省值:128
同时进入SqlMapClient.startTransaction()的最大线程数。大于这个值的线程将阻塞直到另一个线程退出。不同的DBMS 有不同的限制值。这个值应该总是小于或等于maxSessions,而且要比maxRequests小的多。通常情况下,减少这个值可以提高执行的效率。
例子:maxTransaction="16"
缺省值:32
启用或禁用SqlMapClient所有的cache models。调试程序时有用。
例子:cacheModelsEnabled="true"
缺省值:true
启用或禁用SqlMapClient所有的lazy loading。调试程序时有用。
例子:lazyLoadingEnabled="true"
缺省值:true
This setting enables runtime bytecode enhancement to facilitate optimized JavaBean property access as well as enhanced lazy loading.
例子 enhancementEnabled="true"
缺省值: false (disabled)
这个选项如果启用,你就必须使用全限定名来引用mapped statements,这个全名是由sqlMap的名字和statement的名字组成的。
例如:queryForObject("sqlMapName.statementName");
缺省值: false (disabled)
例如:
下面是在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
SIMPLE com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
DBCP com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
JNDI com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory
注意:在Sql Map 1.0版本中,允许同时配置多个数据源,这样会导致一些问题。所以从2.0的版本开始,只允许配置一个数据源,如果你有配置多个数据源的需求的话,建议你使用多个不同配置的属性文件,或者在build Sql Map的时候,作为一个参数传进来。
1. JDBC
允许用JDBC通过Connection 的commit()和rollback()方法来控制事务。
这种事务管理使用一个JTA的全局事务将SQL Map的activities作为一个wider scope事务的一部分而包含进来,这个事务可能包含其他的数据库或事务
源。这个配置需要一个UserTransaction属性来通过JNDI resource设置本地的user transaction。
允许你自己管理事务。你仍然可以配置一个数据源,但是事务不会在框架生命周期中被提交或回滚。这意味着你必须要用自己的程序来控制事务。这个设置对于非事务型数据库是非常有用的。
目前本框架提供三种数据源工厂,但是你也可以自己写一个。下面是每一种数据源工厂的配置举例:
SimpleDataSourceFactory 为池化的DataSource提供了一个基本的实现,适用于在没有容器提供数据源的情况。
DbcpDataSourceFactory 实现使用Jakarta DBCP(Database Connection Pool)的DataSource
API 提供连接池服务。适用于Web 容器不提供DataSource 服务的情况,或执行一个单独的
应用。DbcpDataSourceFactory 中必须要配置的参数例子如下:
JndiDataSourceFactory 在应用服务器的容器中从JNDI Context 中查找DataSource 实现。当使用应用
服务器,并且服务器提供了容器管理的连接池和相关的DataSource 实现的情况下,可以使用
JndiDataSourceFactory。使用JDBC DataSource 的标准方法是通过JNDI 来查找。
注意:上面的配置是使用标准的JDBC事务管理。但是,在一个容器管理的数据源中,你也可能想为全局的事务做如下配置:
sqlMap元素用于包括SQL Map 映射文件和其他的SQL Map 配置文件。每个SqlMapClient
对象使用的SQL Map 映射文件都要在此声明。映射文件作为stream resource 从类路径或URL中读
入。您必须相对于类路径或URL来指定所有的SQL Map 文件。
下面是几个例子:
五、Parameter Maps and Inline Parameters
(一) property
(二) jdbcType
(三) javaType
(四) nullValue
(五) Inline Parameter Maps
六、Result Maps
(一) property
(二) column
(三) columnIndex
(四) jdbcType
(五) javaType
(六) nullValue
(七) select 复杂属性
1.1:1关系:
2.1:M与M:N关系:
3.多个复杂参数属性
4.在Parameter Maps and Result Maps中支持的参数
七、缓存Mapped Statement Result
(一)Read-Only 与 Read/Write
(二)Serializable Read/Write Caches
(三)缓冲类型