文章转载自互联网,如果您觉得我们侵权了,请联系 管理员,我们会立刻处理。
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的配置如下:
         
      
      
      
      
      
调用代码如下:
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)
如果上面的配置如下:
         
      
      
      
      
      
调用代码如下:
Author author = (Author)sqlMapClient.queryForList("getAuthor5", new Integer(1));
如果SELECT * FROM author WHERE auth_id > 1的记录有N条,那么将对id="getLinkArticle1"的语句执行N次查询,这样所有的查询总数将为N+1次,执行效率会很低。
这时,可以使用下面的联合查询的方法来解决:
         
      
      
      
      
      
调用代码为:
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;
    }
... ...
}
配置如下:
         
      
      
      
      
      
调用代码为:
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}。下面是一个例子:
   
   
   
   
   
   
你也可以只写字段的名称,只要按照所关联的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]