文章转载自互联网,如果您觉得我们侵权了,请联系 管理员,我们会立刻处理。
Parameter Maps and Inline Parameters
[nullValue="NUMERIC"] [null="-9999999"]/>
括号[]中是可选的属性。parameterMap 元素的id 属性作为唯一标识,在同一个SQL Map XML 文件中不能重名。一个parameterMap 可包含任意多的property 元素。
一、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
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 值写入数据库。
例如:
INSERT INTO author (auth_name,auth_age,auth_tel,auth_address) VALUES (?,?,?,?)
您可以在另一个SQL Map XML 文件中引用parameterMap。例如,要在另一个文件中引用上面的parameterMap,可以使用名称"Product.insert-product-param"。
五、Inline Parameter Maps
使用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元素,这样会使代码更清晰。
六、Result Maps
在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
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,您可以这样做:
在上例中,如果取得的记录中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的配置如下:
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语句就可以解决。
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;
}
... ...
}
配置如下:
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 的情况,但是到目前位置,还没有其他的方法来解决这个问题,希望在不久的将来能够解决。
3.多个复杂参数属性
你可能已经注意到了,上面的例子中,在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][email protected][/email]
注意:有些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
上面的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 实现可以这样配置:
MEMORY cache 实现只认识一个元素。这个名为“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 中清除。
值得注意的是,这里指的对象可以是任意的,从单一的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 的大小,持久化方法(内存,文件等)和集群方法。
要获得更详细的信息,请参考OSCache 文档。OSCache 及其文档可以从OpenSymphony网站上获取:[url]http://www.opensymphony.com/oscache/[/url]