iBatis.net入门指南

iBatis.net入门指南

 

iBatis.net入门指南    - 1 -

什么是iBatis.net ?    - 3 -

iBatis.net的原理    - 3 -

新人指路    - 3 -

iBatis.net的优缺点    - 4 -

SQL Mapping金典案例    - 5 -

情景一基本标签(insert,update ,select)及属性(parameterClass,resultClass)    - 5 -

情景二子标签(<sql /><include />)及转义处理    - 7 -

情景三高阶标签(<dynamic />)及属性(resultMap)    - 8 -

情景四储存过程标<procedure/>及属性parameterMap    - 10 -

ISqlMapper接口    - 12 -

深度私塾    - 14 -

向sql语句中传入多个参数    - 14 -

自动生成键selectkey    - 14 -

Oracle    - 14 -

SqlServer    - 14 -

MySQL    - 14 -

SQLite    - 14 -

储存过程调用方法    - 15 -

SqlServer    - 15 -

Oracle    - 15 -

Reference    - 16 -

 

 

 

参与人员:

作者

联系方式

毛凌志

Frederick.mao@gmail.com

   

 

发布记录

版本

日期

作者

说明

0.0

2009-08-21

毛凌志

初稿

       

 

什么是iBatis.net ?

 

以SQL Mapping为核心,提供基于ado.net之上的一层简单抽象,将数据库中数据映射到领域模型的持久层框架

iBatis.net的原理

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

新人指路

  1. 新建工程,添加2个引用文件

  1. 添加并修改配置3个配置文件

  1. 根据数据库的表结构及业务设计领域模型(可以手动,或利用codesmith等生成器半自动完成
  2. 由模板或手动生成SQL Mapping文件(e.g.Account.xml),并根据业务需求撰写sql语句

更详细的使用过程,可参见视频。

iBatis.net的优缺点

优点

缺点

  • 集中管理,聚焦,减少代码量
  • 传入参数仅能一个,多于一个需要进行包装
  • 细粒度控制
  • 混合型解决方案
  • 思维方式
  • 非强类型,运行时,非编译时

 

SQL Mapping金典案例

 

    情景一 基本标签(insert,update ,select)及属性(parameterClass,resultClass)

iBatis.net原理一节示例了使用ibatis.net进行insert和select操作,下面展示介绍如何具体的进行SqlMapping。

在SqlMapping的配置文件本例中为Account.xml文件中,添加如下的标签段

    并将此Account.xml文件的路径加入到sqlmap.config中

在iBatis.net中提供了五种常用的标签如下表示

 

表1 基本SQL Mapping标签

标签

说明

<insert></insert> 

删除sql语句

<update></update> 

更新sql语句

<select></select> 

选取sql语句

<delete></delete> 

删除sql语句

<procedure></procedure> 

调用储存过程

 

在这些容器标签中有一些常用的属性如下所示

表2 基本SQL Mapping标签属性

属性

说明

Id

用于唯一标识一条sql语句

resultClass

用于将sql语句返回的字段和resultClass提定的类进行隐式的自动映射

parameterClass

用于指定输入参数的类型,如果输入参数有多个应使用Hashtable或是类进行包装

下面是关于这两条SQL语句的调用方法和测试

    

情景二 子标签(<sql /><include />)及转义处理

在一些复杂的情景中,为了简化SQL的编写工作,ibatis.net提供了一些标签用于简化SQL的编写操作。可以将可复用的sql语句写在<sql></sql>标签中,然后中<select/><insert/><update/><delete/>等标签中使用<include refid="xxx" />进行引用,其中refid属性指向<sql/>子标签的标识id。

如下例中所示

在此例中,我们还使用了了<![CDATA[]]>,主要是在sql语句中有些特殊的字符如<>等,在xml中不能直接使用,因为我们将包含特殊字符的 SQL语句放入XML的CDATA块中

在此例中,通过Hashtable传入了两个参数,下面介绍了程序中是如何调用上面这段代码的

    

情景三 高阶标签(<dynamic />)及属性(resultMap)

    当在进行一些组合查询等需要灵活组装sql的情景时,需要用到dynamic标签。如下所示。

这段代码可以大至翻译为如下的伪代码

Account acc=new Account();

acc.Item="买菜";

if(acc.Item!=null)

sql+="where item like '%"+acc.Item+"%'";

If(acc.Money!=null)

sql+="or Money like '%20%';

this.Response.Write(sql)

 

表3 <dynamic>元素

名称

描述

isEqual 

如果参数相等于值则查询条件有效

isNotEqual 

如果参数不等于值则查询条件有效

isGreaterThan 

如果参数大于值则查询条件有效

isGreaterEqual 

如果参数大于等于值则查询条件有效

isLessEqual 

如果参数小于值则查询条件有效

isPropertyAvaiable

如果参数可用则查询条件有效

isNotPropertyAvaiable 

如果参数不可用则查询条件有效

isNull 

如果参数为Null则查询条件有效

isNotNull 

如果参数不为Null则查询条件有效

isEmpty 

如果参数为空则查询条件有效

isNotEmpty 

如果参数不为空则查询条件有效。参数的数据类型为Collectionstring时参数不为 NULLo ""

isParameterPresent 

如果参数类为Null则查询条件有效

isNotParameterPresent

如果参数类不为Null则查询条件有效

 

在上面的属性中使用了resultMap属性,原先使用resultClass是自动隐式的将sql语句映射出的字段。而resultMap属性用于将select输出的语句进行显示的映射, 如下所示

将sql语句取出数据的字段名名称和领域模型进行一个显示的映射。

情景四 储存过程标<procedure/>及属性parameterMap

在SQL Mapping中专门为储存过程提供了标签,如下所示:

其调用方法是在容器标签中撰写储存过程的名称,此写法适合SQL Server,针对其它数据库的方法参见储存过程调用方法

注意到在这里我们使用了一个新的属性parameterMap,它的作用是对输入的参数进行显示的映射,之前在情景一中介绍过parameterClass,它和parameterMap类似均是用来对传入sql的参数时行映射,不同的是对parameterClass是隐式映射的,而parameterMap是显示的映射。

注意到,有一个direction属性默认为储存过程的in,可以特别指定用于其它的方式如out或inout等。

在这里我们也使用了resultMap属性,该属性已在情景三中进行了介绍,读者忘了可以再温习一下

 

关于储存过程的具体的调用方法参见如下的示例,该储存过程的作用是对Accounts表进行分页返回指定页面的记录数和总的记录数。

ISqlMapper接口

当我们执行SQL语句取出需要的数据库,也做了SQL Mapping的映射,最终我们的目的是希望将取出的数据以我们需要的形式(或者说是数据结构)呈现给我们。那么ISqlMapper接口就是来实现这一操作的,如下图所示。

方法

说明

QueryForObject<T>(string,object) 

Overloaded. Executes a Sql SELECT statement that returns a single object of the type of the resultObject parameter.

QueryForList<T>(string,object)

Overloaded. Executes a Sql SELECT statement that returns data to populate a number of result objects. The parameter object is generally used to supply the input data for the WHERE clause parameter(s) of the SELECT statement.

QueryForDictionary<K,V>(string,object,string,string)

Overloaded. Executes the SQL and retuns all rows selected in a map that is keyed on the property named in the keyProperty parameter. The value at each key will be the value of the property specified in the valueProperty parameter. If valueProperty is null, the entire result object will be entered.

Insert(string,object)

Executes a Sql INSERT statement. Insert is a bit different from other update methods, as it provides facilities for returning the primary key of the newly inserted row (rather than the effected rows). This functionality is of course optional. The parameter object is generally used to supply the input data for the INSERT values.

Update(string,object)

Executes a Sql UPDATE statement. Update can also be used for any other update statement type, such as inserts and deletes. Update returns the number of rows effected. The parameter object is generally used to supply the input data for the UPDATE values as well as the WHERE clause parameter(s).

Delete(string,object)

Executes a Sql DELETE statement. Delete returns the number of rows effected.

深度私塾

 

向sql语句中传入多个参数

    参见情景二

自动生成键selectkey

在数据库插入一条数据的时候,经常是需要返回插入这条数据的主键。但是数据库供应商之间生成主键的方式都不一样。

有些是预先生成(pre-generate)主键的,如Oracle和PostgreSQL;有些是事后生成(post-generate)主键的,如MySQL和SQL Server。但是不管是哪种方式,我们都可以用iBATIS的节点来获取语句所产生的主键。

Oracle

<insert id="insertProduct-ORACLE" parameterClass="product">

<selectKey resultClass="int" type="pre" keyProperty="id" >

SELECT STOCKIDSEQUENCE.NEXTVAL AS VALUE FROM DUAL

selectKey>

insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#)

insert>

SqlServer

<insert id="insertProduct-MS-SQL" parameterClass="product">

insert into PRODUCT (PRD_DESCRIPTION)

values (#description#)

<selectKey resultClass="int" type="post" keyProperty="id" >

select @@IDENTITY as value

</selectKey>

    </insert>

MySQL

<insert id="insertProduct-MYSQL" parameterClass="product">

insert into PRODUCT (PRD_DESCRIPTION)

values (#description#)

<selectKey resultClass="int" type="post" keyProperty="id" >

select LAST_INSERT_ID() as value

</selectKey>

</insert>

SQLite

<insert id="Create" parameterClass="Subject">

INSERT INTO SUBJECT

(SubjectName,QuestionCount,IsNowPaper)

VALUES(#SubjectName#,#QuestionCount#,#IsNowPaper#)

<selectKey resultClass="int" type="post" property="SubjectId">

SELECT seq

FROM sqlite_sequence

WHERE (name = 'SUBJECT')

</selectKey>

</insert>

储存过程调用方法

SqlServer

        <parameterMap id="pm_ErickPagination" class="System.Collections.Hashtable">

            <parameter property="PageSize"/>

            <parameter property="CurrentPage"/>

            <parameter property="TotalCount" column="TotalCount" direction="Output" />

        </parameterMap>

 

        <procedure id="sp_ErickPagination"

                 parameterMap="pm_ErickPagination"

                 resultMap="FindAllResult">

            ErickPagination

        </procedure>

 

Oracle

 

    <parameterMap id="userParam" class="user">

        <parameter property="username"/>

        <parameter property="email"/>

    </parameterMap>

 

    <procedure id="sendEmail" parameterMap="userParam">

        {call sendMail(?,?)}

    </procedure>

Reference

 

 
posted @ 2011-05-21 19:21 awp110 阅读(182) 评论(0) 编辑
 
 

关键词:IBatis.NET Access mdb cast typeHandler 类型转换

这两天被一个问题折磨得死去活来,终于解决了,写下来以备参考:


问题是这样的:


我在项目中使用了
IBatis.Net,数据库使用的是 MS Access。因为Access数据库没有floatdouble类型,只有Currency类型可以用作浮点数。所以我定义了类似如下的对象,表,以及SQL语句:

1.对象

        public class Mark

        {

                public string Subject{...}

                public int Year{...}

                public double Point{...}

        }

 

 

2.数据库Mark

Subject       Text

Year           Number

Point          Currency

 

3.SQL statement

        <statements>

                <select id="Query" parameterMap="pMarkMap" resultMap="rMarkMap">

                        Select Subject, Year, Point

                        From Mark

                        <dynamic prepend ="Where">

                                <isNotNull prepend="AND" property="Subject">

                                        Subject = #Subject#

                                </isNotNull>

                                <isNotNull prepend="AND" property="Year">

                                        Year = #Year#

                                </isNotNull>

                        </dynamic>

                </select>

        </statements>

 

4.查询数据库

        Mark mark = new Mark();

        mark.Year = 2005;

        ISqlMapper mapper = Mapper.Instance();

        Object obj = mapper.QueryForObject("Mark.Query", mark);

        mark = obj as Mark;

        ArrayList list = new ArrayList();

        list.Add(obj);

 

        Grid.DataSource = list;

 

结果抛出异常:"Specified cast is not valid."

这个问题困扰了我两天,最后才发现两个解决办法:

1.      将对象中的Point属性的类型改成 decimal. 这种方法固然简单,可是在数据库中使用Currency乃不得已,在SQL Server中却有Float类型可以使用,IBatis自动支持从.NETdouble类型到数据库Float类型的转换。所以如果为了Currency而使用decimal,则后台数据库变成SQLServer或是Oracle时,在数据库端不得不使用Decimal/Money等类型。或者修改程序中的decimal定义为double类型,这都不是很合理。所以,下面是一个相对复杂一点却合理的解决方法。

注:IBatis.Net自动支持的类型转换请参阅<<DataMapper Developer Guide>> version 1.5.0 Chapter 3.6, 3.7: Supported database types

 

2.     

使用自定义类型转换函数

 

·         

定义类

using System;

using IBatisNet.Common;

using IBatisNet.DataMapper.TypeHandlers;

 

namespace TestIBatis

{

        public class DoubleCurrencyTypeHandler :

                IBatisNet.DataMapper.TypeHandlers.ITypeHandlerCallback

        {

                #region ITypeHandlerCallback Members

 

// 此类型的null值

                public object NullValue

                {

                        get

                        {

                                return null;

                        }

                }

 

                public object ValueOf(string s)

                {

                        // 这个函数用于将nullValue值翻译成要比较的null值

// 如果没有,则推荐返回字符串s

                        return s;

                }

 

                public object GetResult(IResultGetter getter)

                {

                        // 用于将从数据库读取的值转换成.NET中的值

                        // 这里我们知道Currency可以转成decimal类型,

// 再用显示转换将decimal转换成double

                        decimal v1 = Convert.ToDecimal(getter.Value);

                        double v2 = (double)v1;

                        return v2;

                }

 

                public void SetParameter(IParameterSetter setter, object parameter)

                {

                        // TODO: 将.NET中的double型转换成decimal,再转换成Currency

                        decimal v1 = Convert.ToDecimal(parameter);

                        setter.Value = v1;

                }

                #endregion

        }

 

}

 

 

·          定义SQL中的parameterMap 及 resultMap

SqlMap.config中加入下面的语句

 <alias>

        <typeAlias alias="DoubleCurrency"

type="TestIBatis.DoubleCurrencyTypeHandler, TestIBatis" />

 </alias>

 

 <typeHandlers>

        <typeHandler type="double" dbType="Currency" callback="DoubleCurrency" />

 </typeHandlers>

 

SQL statement所在的Mark.xml文件里加上如下语句

        <alias>

                <typeAlias alias="Mark" type="TestIBatis.Mark, TestIBatis" />

        </alias>

       

       

        <parameterMaps>

                <parameterMap id="pMarkMap" class="Mark">

                        <parameter property="Subject" column="Subject" />

                        <parameter property="Year" column="Year"

type="Int32" dbType="Integer" />

                        <parameter property="Point" column="Point"

type="double" dbType="Currency" />

                </parameterMap>

        </parameterMaps>

       

        <resultMaps>

                <resultMap id="rMarkMap" class="Mark">

                        <result property="Subject" column="Subject" />

                        <result property="Year" column="Year" type="Int32" dbType="Integer" />

                        <result property="Point" column="Point"

type="double" dbType="Currency" />

                </resultMap>

        </resultMaps>

         

        <statements>

                <select id="Query" parameterMap="pMarkMap" resultMap="rMarkMap">

                        Select Subject, Year, Point

                        From Mark

                        <dynamic prepend ="Where">

                                <isNotNull prepend="AND" property="Subject">

                                        Subject = #Subject#

                                </isNotNull>

                                <isNotNull prepend="AND" property="Year">

                                        Year = #Year#

                                </isNotNull>

                        </dynamic>

                </select>

        </statements>

 

运行程序,一切正常

 

注:关于自定义类型转换,请参阅<<DataMapper Developer Guide>> version 1.5.0 Chapter 3.5.5 Custom Type Handlers

 
posted @ 2011-05-21 18:55 awp110 阅读(104) 评论(0) 编辑
 
 

大概一年左右的时间里Club数据库的CPU一直处于很高的负荷中,从40%一直攀升到如今的80%,随着数据量的增加,负担越来越重,已经频繁超时,且濒临无法服务的边缘。

经长期的调查发现这是Ibatis.net的一个性能问题(同样适用于Ibatis)。

问题是这样的:Club的主要业务表是Comment表,其中的主键是一个varchar(36)类型的Guid,当每次搜索一条记录时我们会使用这样的一个statement:

<select id="GetComment" parameterClass="string" resultMap="CommentResult">

select Id, ReferenceId, ReferenceName, ReferenceType, ReferenceImage, ReferenceTime, Title, Pros, Cons, Content, Score, ViewCount, ReplyCount, UsefulVoteCount, UselessVoteCount, UserId, UserImage, UserLevelId, UserRegisterTime, UserProvince, UserIp, CreationTime, Status, Remark

from Comment with (nolock)

where Id = #value# and Status >= 0

</select>

虽然看着十分正常,但是却是问题的关键,因为parameterClass是string,当Ibatis.net动态组成的sql语句会将其默认定为一个nvchar(36)来对数据库进行查询,这样与原有数据库的类型不一样,

造成了全表扫描,解决办法是制定传入类型的dbType(inline方法为):

<select id="GetComment" parameterClass="string" resultMap="CommentResult">

select Id, ReferenceId, ReferenceName, ReferenceType, ReferenceImage, ReferenceTime, Title, Pros, Cons, Content, Score, ViewCount, ReplyCount, UsefulVoteCount, UselessVoteCount, UserId, UserImage, UserLevelId, UserRegisterTime, UserProvince, UserIp, CreationTime, Status, Remark

from Comment with (nolock)

where Id = #value:VarChar# and Status >= 0

</select>

当然也可以使用parameterMap来解决(具体参考Ibatis手册)。通过这个办法Club数据库压力已经只有17%左右,非常有效。我认为在string作为参数且数据库字段是一个varchar的时候都可以使用该方法,且效率很高。

 
posted @ 2011-05-21 15:43 awp110 阅读(43) 评论(0) 编辑
 
 
Castle是另外一个框架,包含了AOP、IOC、ORM等多个方面,其中的Castle.DynamicProxy可以实现动态代理的功能,这个也是很多框架的基础。在IBatis.Net中就是使用了Castle.DynamicProxy来实现数据库连接等动态操作的。同时在NHibernet等其他框架中也使用到了这个技术。
下面我通过一个简单例子来看一下如何在我们的代码中调用Castle.DynamicProxy:
一般情况下要有三个类:
1、接口类:
using System;
using System.Collections.Generic;
using System.Text;

namespace GSpring.CastleTest
{
    
public interface ITest
    
{
        
string GetName(string pre);
    }

}

2、实现类:
using System;
using System.Collections.Generic;
using System.Text;

namespace GSpring.CastleTest
{
    
public class Test : ITest
    
{
        
public string GetName(string pre)
        
{
            
return pre + ",GSpring";
        }

    }

}

这两个都很普通的接口和实现
3、代理类:
using System;
using System.Collections;
using System.Reflection;
using Castle.DynamicProxy;

namespace GSpring.CastleTest
{
    
/// <summary>
    
/// Summary description for DaoProxy.
    
/// </summary>

    public class InterceptorProxy : IInterceptor
    
{
         
public object Intercept(IInvocation invocation, params object[] arguments)
        
{
            Object result 
= null;

            
//这里可以进行数据库连接、打日志、异常处理、权限判断等共通操作
            result = invocation.Proceed(arguments);

            
return result;
        }


    }

}

这个类首先实现接口IInterceptor,然后就可以在方法Intercept中加入我们自己的逻辑

然后看一下调用的方式:
        ProxyGenerator proxyGenerator = new ProxyGenerator();
        IInterceptor handler 
= new InterceptorProxy();
        Type[] interfaces 
= typeof(ITest) };
        Test test 
= new Test();
        ITest iTest 
= (proxyGenerator.CreateProxy(interfaces, handler, test) as ITest);
        
string result = iTest.GetName("Hello");
最后一句调用的地方,实际会首先执行InterceptorProxy类中的Intercept方法。
 
posted @ 2011-05-21 15:39 awp110 阅读(54) 评论(0) 编辑
 
 
其实调用方式比较简单,主要也就是两种类型的存储过程:
1、更新类型的存储过程
2、查询类型的存储过程
下面就来看看具体的调用方式:
1、更新类型的存储过程
sp_InsertAccount:
CREATE PROCEDURE [dbo].[sp_InsertAccount]
    
-- Add the parameters for the stored procedure here
   @Account_ID int,
   
@Account_FirstName varchar(32),
   
@Account_LastName varchar(32)AS
BEGIN
insert into accounts (account_id, account_firstname, account_lastname) 
    
values (@Account_ID,@Account_FirstName,@Account_LastName )
END
Map配置文件:
        <procedure id="InsertAccountViaStoreProcedure" parameterMap="insert-params_new">
            sp_InsertAccount
        
</procedure>

    
<parameterMap id="insert-params_new" class="Account">
      
<parameter property="Id" />
      
<parameter property="FirstName" />
      
<parameter property="LastName" />
    
</parameterMap>

这里要注意的就是ParameterMap中的参数个数和顺序要和sp_InsertAccount存储过程中的一致

Ado中的调用代码:

        public void InsertAccountViaStoreProcedure(Account account)
        
{
            
try
            
{
                sqlMap.Insert(
"InsertAccountViaStoreProcedure", account);
            }

            
catch (DataAccessException ex)
            
{
                
throw new DataAccessException("Error executing InsertAccountViaStoreProcedure. Cause :" + ex.Message, ex);
            }

        }

这里使用的是sqlMap.Insert的方法,为了看起来直观一点,其实使用sqlMap.QueryForObject方法的话效果也是一样的:)

2、查询类型的存储过程
GetAccountByName

CREATE PROCEDURE [dbo].[GetAccountByName]
    
@name varchar(32)
AS
BEGIN
select * from accounts where Account_FirstName like '%' + @name + '%'
END

Map配置文件:
    <procedure id="GetAccountByNameViaStoreProcedure" resultMap="account-result" parameterMap="selectpro-params">
      GetAccountByName
    
</procedure>

    
<parameterMap id="selectpro-params" class="string">
      
<parameter property="name"/>
    
</parameterMap>
这里parameterMap也是和上面的要求一样,至于property的名字在这里没有实际作用,可以任意取名的

Ado中的调用代码:
        public ArrayList GetAccountByNameViaStoreProcedure(string strName)
        
{
            
try
            
{
                ArrayList list 
= (ArrayList)sqlMap.QueryForList("GetAccountByNameViaStoreProcedure", strName);
                
return list;
            }

            
catch (DataAccessException ex)
            
{
                
throw new DataAccessException("Error executing SqlAccountViaSqlMapDao.GetAccountById. Cause :" + ex.Message, ex);
            }

        }
 
posted @ 2011-05-21 15:39 awp110 阅读(21) 评论(0) 编辑
 
 
在IBatis.Net中可以通过配置文件动态选择数据库、动态选择Dao对象。
Dao对象也就是操作数据库的类,通过配置文件我们可以选择DataMapper的方式、Ado的方式、NHibernet的方式以前其他第三方的方式来操作数据库。有利于系统的灵活性和可扩展性。
通过分析动态选择Dao的设计可以加深对IBatis.Net的理解,更好的使用它,同时也可以借鉴它的好的设计模式,应用到我们的程序开发中去。

源代码是最好的分析方式,下面是一些重点代码和说明:
前提:需要在dao.config中配置:
    <daoFactory>
            
<dao interface="GSpring.Dao.Interfaces.IAccountDao, GSpring.Dao" implementation="GSpring.Dao.Implementations.AccountDao, GSpring.Dao"/>
    
</daoFactory>

在代码中首先需要进行初始化:
    DomDaoManagerBuilder builder = new DomDaoManagerBuilder();
    builder.Configure(
"dao.config");
这段代码实际上做了很多事情,其中就有:将所有的配置的dao的接口和实现注册到DaoManager类的静态属性中去。也就是在整个应用程序或网站启动时注册一次就可以了,以后直接从静态属性中取出来使用就可以了。

和dao注册相关的代码如下:
        dao.Implementation = NodeUtils.GetStringAttribute(prop, "implementation");
        dao.Interface 
= NodeUtils.GetStringAttribute(prop, "interface");
        
        _daoInstance 
= _daoImplementation.GetConstructor(Type.EmptyTypes).Invoke(nullas IDao;
        _proxy 
= DaoProxy.NewInstance(this);
也就是把配置文件中的interface和implementation读取,然后生成代理。

最主要的就是最后一句代码,DaoProxy.NewInstance的实现如下:
            Castle.DynamicProxy.ProxyGenerator proxyGenerator = new ProxyGenerator();
            IInterceptor handler 
= new DaoProxy(dao);
            Type[] interfaces 
= {dao.DaoInterface, typeof(IDao)};

            
return (proxyGenerator.CreateProxy(interfaces, handler, dao.DaoInstance) as IDao);
这里我们看到其中使用了Castle.DynamicProxy中的方法(Castle是另外一个开源框架,我和在以后的博客中再说)
DaoProxy实现IInterceptor接口,也就是AOP中常有的拦截机。以后当我们通过IDao接口调用实际的Dao时,都会先通过DaoProxy,由DaoProxy拦截后进行一些必要的处理,然后再动态决定调用哪一个Dao来进行数据库操作

生成好之后都会放在DaoManager的静态属性中,下次要用的时候直接从里面去就可以了:
        public IDao this[Type daoInterface]
        
{
            
get
            
{
                Dao dao 
= _daoMap[daoInterface] as Dao;
                
if (dao == null
                
{
                    
throw new DataException("There is no DAO implementation found for " + daoInterface.Name + " in this context.");
                }

                IDao idao 
= dao.Proxy;
                
return idao;
            }

        }

以上涉及到的主要的类图如下:
 
posted @ 2011-05-21 15:38 awp110 阅读(38) 评论(0) 编辑
 
 
在项目开发过程中,查询占了很大的一个比重,一个框架的好坏也很多程度上取决于查询的灵活性和效率。
在IBatis.Net中提供了方便的数据库查询方式。

在Dao代码部分主要有两种方式:
1、查询结果为一个对象:
                ISqlMapper sqlMap = sqlMapDaoSession.SqlMap;

                
return (Account) sqlMap.QueryForObject("GetAccountViaColumnName", accountID);
2、查询结果为一个列表:
                ISqlMapper sqlMap = sqlMapDaoSession.SqlMap;

                
return (ArrayList)sqlMap.QueryForList("GetAccountAsHashtableResultClass"1);
这两种方法同时都提供了面向泛型的重载方法。这两个方法的第一个参数对应配置文件中的select id,第二个参数表示传入查询的条件

配置文件的写法:
在IBatis.Net中提供了多种查询配置的写法,我这里列出几种比较常用的方式:
1、获得一张表的所有数据
        <select id="GetAllAccountsAsHashMapViaResultMap"
                        resultMap
="account-hashtable-result">
            select 
*
            from Accounts
            order by Account_ID
        
</select>
这是最简单的方式,其中resultMap是返回查询结果的形式,需要另外配置:
        <resultMap id="account-hashtable-result" class="Hashtable">
            
<result property="Id"           column="Account_ID"/>
            
<result property="FirstName"    column="Account_FirstName"/>
            
<result property="LastName"     column="Account_LastName"/>
            
<result property="EmailAddress" column="Account_Email"/>
        
</resultMap>
表示:得到的结果的每一条记录都映射成一个Hashtable,这个Hashtable中包含四个Key(Id,FirstName......)

2、根据条件查询(简单方式):
        <select id="GetAccountViaColumnIndex"
                parameterClass
="int"
                resultMap
="indexed-account-result">
            select
            Account_ID,
            Account_FirstName,
            Account_LastName,
            Account_Email
            from Accounts
            where Account_ID 
= #value#
        
</select>
只有一个条件,传入参数的类型是int型,拼写sql时直接用 #value#就可以了

3、根据条件查询(较复杂方式):
        <select id="GetAccountsDynamic" resultMap="account-result" parameterClass="Hashtable" >
            select top $MaximumAllowed$ 
* from Accounts
            
<dynamic prepend="where">
                    
<isParameterPresent>
                    
<isNotEmpty prepend="and" property="FirstName" >
                            Account_FirstName LIKE 
'%$FirstName$%'
                    
</isNotEmpty>
                    
<isNotEmpty prepend="and" property="LastName" >
                            Account_LastName LIKE 
'%$LastName$%'
                    
</isNotEmpty>
                    
<isNotEmpty prepend="and" property="EmailAddress"  >
                            Account_Email LIKE 
'%$EmailAddress$%'
                    
</isNotEmpty>
                    
</isParameterPresent>
                
</dynamic>
                order by Account_LastName
        
</select>
传入参数是一个Hashtable,MaximumAllowed等表示的是Hashtable里的key值,用$$包含起来。
并且查询时可以根据条件是否为空动态拼写sql语句
PS:输入参数同样可以使用Account类,注意对应的键要和类中的属性名一致(大小写也要一样)

4、多表查询
多表查询时返回参数有三种方式,一种是新建一个类,在这个类中包含这多个表的所有属性,还有一种就是直接返回Hastable就可以了:
        <select id="GetAccountAsHashtableResultClass"
    resultClass
="HashMap">
            select
            a.
*,b.*
            from a,b
            where a.Account_ID 
= b.Account_ID        </select>
PS:这里的HashMap实际上就是Hashtable

第三种方式是使用IBatis中的复杂属性(感谢Anders Cui 的提醒)
比如现在有两张表Account和Degree,使用Account_ID关联,那么需要在原有的基础上修改:
1、修改Account实体类,加入一个属性:
        private Degree _degree;
        
public Degree Degree
        
{
            
get
            
{
                
return _degree;
            }

            
set
            
{
                _degree 
= value;
            }

        }
这样是一个1:1的关系,也可以加入IList DegreeList的属性,这样查询的结果就是一个1:n的关系

2、修改配置文件:
resultMaps节加入:
    <resultMap id="comresult"  class="Account" >
      
<result property="Id"           column="Account_ID"/>
      
<result property="FirstName"    column="Account_FirstName"/>
      
<result property="LastName"     column="Account_LastName"/>
      
<result property="EmailAddress" column="Account_Email" nullValue="no_email@provided.com"/>
      
<result property="Degree" column="Account_ID=Account_ID"  select="degreeretrive" />
    
</resultMap>
对于Degree属性,还可以加入lazyLoad=true 延迟加载,优化性能(也就是开始时并没有实际查询数据库,当用到属性Degree时,才实际的查询相应的数据)

statements节加入:
    <statement id="degreeretrive"
      parameterClass
="Hashtable"
      resultClass
="Degree">
      select 
*
      from Degree
      where Account_id 
= #Account_ID#
    
</statement>

    
<select id="GetComTables"
      resultMap
="comresult">
      select 
*
      from Accounts
      order by Account_ID
    
</select>
这样可以正确的查询出结果,符合OO,但是也有两个小问题:
1、比较麻烦,不够灵活
2、性能受影响:
    这种方式其实和Hibernet比较类似了,查询时首先执行
    select *        from Accounts        order by Account_ID
    然后根据这条语句的结果,比如有100条记录,那就要执行100次以下的语句:
    select *        from Degree        where Account_id =  @param0

关于输入输出:
从上面可以看到输入时可以使用:parameterClass和parameterMap,输出时可以使用:resultClass和resultMap
对于resultMap和parameterMap我们需要另外进行配置(如上所示)
对于parameterClass和resultClass,如果是C#固有类型可以直接使用,如果是我们自定义类可以在SqlMap.config中先统一声明一下:
    <alias>
        
<typeAlias alias="Account" type="GSpring.Domain.Account"/>
    
</alias>
 
posted @ 2011-05-21 15:37 awp110 阅读(36) 评论(0) 编辑
 
 
在上一篇文章中我提到了三种方式,都是各有利弊:
第一种方式当数据关联很多的情况下,实体类会很复杂;
第二种方式比较灵活,但是不太符合OO的思想(不过,可以适当使用);
第三种方式最主要的问题就是性能不太理想,配置比较麻烦。

下面是第四种多表查询的方式,相对第二种多了一点配置,但是其他方面都很好
(当然可能还有其他更好地解决方法,希望能多提宝贵意见-_-)
例子还是一样:两张表Account和Degree,使用Account_ID关联,需要查出两张表的所有纪录

首先:修改实体类,增加以下属性:
        private Degree _degree;
        
public Degree Degree
        
{
            
get
            
{
                
return _degree;
            }

            
set
            
{
                _degree 
= value;
            }

        }
(和第三种方法一样)

然后:修改配置文件,这也是最重要的地方(PS:IBatis.Net中的配置文件真的很强)
resultMaps节加入:
    <resultMap id="com2result"  class="Account" >
      
<result property="Id"           column="Account_ID"/>
      
<result property="FirstName"    column="Account_FirstName"/>
      
<result property="LastName"     column="Account_LastName"/>
      
<result property="EmailAddress" column="Account_Email" nullValue="no_email@provided.com"/>
      
<result property="Degree"  resultMapping="Account.Degree-result"/>
    
</resultMap>

    
<resultMap id="Degree-result"  class="Degree">
      
<result property="Id"           column="Account_ID"/>
      
<result property="DegreeName"    column="DegreeName"/>
    
</resultMap>
这里最主要的就是使用了resultMapping属性,resultMapping="Account.Degree-result",其中Account是当前配置文件的namespace:
<sqlMap namespace="Account"  ......


statements节加入:
    <select id="GetCom2Tables"
     resultMap
="com2result">
      select Accounts.
*, Degree.*
      from Accounts,Degree
      where Accounts.Account_ID 
= Degree.Account_ID
    
</select>
这样就可以随心所欲的写自己需要的sql,性能也很好,不会出现第三种方法中的1+n条的查询语句了。
 
posted @ 2011-05-21 15:37 awp110 阅读(12) 评论(0) 编辑
 
 

在IBatis中提供了数据库缓存的模式,可以提高访问效率。对于一些不常更新的表可以直接利用IBatis的缓存方式。

要使用IBatis的数据库缓存,只要利用配置文件就可以了,实现起来比较简单:

        <select id="GetCachedAccountsViaResultMap"
                    resultMap
="account-result"
                    cacheModel
="account-cache" >
            select 
*
            from Accounts
            order by Account_ID
        
</select>
最主要的就是cacheModel="account-cache",指定缓存的方式,如下,是具体配置缓存的地方:
    <cacheModels>
        
<cacheModel id="account-cache" implementation="MEMORY" >
            
<flushInterval hours="24"/>
            
<flushOnExecute  statement="UpdateAccountViaInlineParameters"/>
            
<flushOnExecute  statement="UpdateAccountViaParameterMap"/>
      
<flushOnExecute  statement="InsertAccountViaParameterMap"/>
      
<property name="Type" value="Weak"/>
        
</cacheModel>        
    
</cacheModels>

其中:implementation="MEMORY"是设置缓存的实现方式,可以指定LRU、FIFO等,有点类似于内存的页替换策略。MEMORY是最常使用的一种方式。

flushOnExecute设置的是当执行了这些语句时更新缓存。

配置好之后我进行了一个简单的测试,基本上是可以的,但也有一点问题:
1、第一次查询结果是4条记录,当我手工往数据库中插入一条记录时,第二次查询还是4条记录
2、当我把系统时间改成第二天(24小时后),再查,得到的结果是5条记录
3、当我执行了InsertAccountViaParameterMap语句插入一条记录时,再查询得到的是6条记录

也就是说:当系统中的表从不进行手工维护,也不由第三方程序修改时,可以使用数据库缓存的方式提高效率

 
posted @ 2011-05-21 15:36 awp110 阅读(16) 评论(1) 编辑
 
 
在IBatis中我们可以灵活的选择DAO类型,也就是可以在底层选用不同的数据库操作方式。有常规方式、配置文件的方式、Hibernet的方式等:
1、常规方式
和我们之前的ADO.NET开发较为类似,都是将sql语句写在cs代码中进行调用:
首先通过配置文件初始化:
            DomDaoManagerBuilder builder = new DomDaoManagerBuilder();
            builder.Configure(
"dao" + "_" + ConfigurationManager.AppSettings["database"+ "_"
                
+ ConfigurationManager.AppSettings["providerType"+ ".config");
            daoManager 
= DaoManager.GetInstance("SimpleDao");        
相对应的配置文件如下:
    <context id="SimpleDao" default="true">
        
<properties resource="http://www.cnblogs.com/database.config"/>
        
        
<!-- ==== SqlClient configuration (default provider) =========    -->
        
<database>
            
<!-- Optional ( default ) -->
            
<provider name="sqlServer1.1"/>
            
<dataSource name="iBatisNet" connectionString="data source=${datasource};database=${database};user id=${userid};password=${password};connection reset=false;connection lifetime=5; min pool size=1; max pool size=50"/>
        
</database>
        
        
<daoFactory>
            
<dao interface="IBatisNet.DataAccess.Test.Dao.Interfaces.IAccountDao, IBatisNet.DataAccess.Test" implementation="IBatisNet.DataAccess.Test.Dao.Implementations.Ado.AccountDao, IBatisNet.DataAccess.Test"/>
        
</daoFactory>
    
</context>
然后在对应的,比如AccountDao中写具体的查询sql等

2、配置方式
将sql语句放在配置文件中,书写和修改较灵活,这也是比较常用的方式
首先通过配置文件初始化:
            DomDaoManagerBuilder builder = new DomDaoManagerBuilder();
            builder.Configure(
"dao" + "_" + ConfigurationManager.AppSettings["database"+ "_"
                
+ ConfigurationManager.AppSettings["providerType"+ ".config");
            daoManager 
= DaoManager.GetInstance("SqlMapDao");        
相对应的配置文件如下:
    <context id="SqlMapDao">
        
<properties resource="http://www.cnblogs.com/database.config"/>
        
<!-- ==== SqlClient configuration =========    -->
        
<database>
            
<dataSource name="iBatisNet" connectionString="data source=${datasource};database=${database};user id=${userid};password=${password};connection reset=false;connection lifetime=5; min pool size=1; max pool size=50"/>
        
</database>        
        
        
<daoSessionHandler id="SqlMap">
            
<!--             -->
            
<property name="resource" value="SqlMap_MSSQL_SqlClient.config"/>

            
<!-- <property name="url" value="E:\Projet\iBatis\trunk\cs\mapper\IBatisNet.DataAccess.Test\bin\Debug\SqlMap_MSSQL_SqlClient.config"/>
            
-->
            
<!-- 
            
<property name="embedded" value="bin.Debug.SqlMap_MSSQL_SqlClient.config, IBatisNet.DataAccess.Test"/> 
            
-->
        
</daoSessionHandler>
        
        
<daoFactory>
            
<dao interface="IBatisNet.DataAccess.Test.Dao.Interfaces.IAccountDao, IBatisNet.DataAccess.Test" implementation="IBatisNet.DataAccess.Test.Dao.Implementations.DataMapper.AccountDao, IBatisNet.DataAccess.Test"/>
        
</daoFactory>
    
</context>
然后可以将每一张表的sql语句单独放在一个配置文件中,比如:
        <select id="GetAccountsDynamic" resultMap="account-result" parameterClass="Hashtable" >
            select top $MaximumAllowed$ 
* from Accounts
            
<dynamic prepend="where">
                    
<isParameterPresent>
                    
<isNotEmpty prepend="and" property="FirstName" >
                            Account_FirstName LIKE 
'%$FirstName$%'
                    
</isNotEmpty>
                    
<isNotEmpty prepend="and" property="LastName" >
                            Account_LastName LIKE 
'%$LastName$%'
                    
</isNotEmpty>
                    
<isNotEmpty prepend="and" property="EmailAddress"  >
                            Account_Email LIKE 
'%$EmailAddress$%'
                    
</isNotEmpty>
                    
</isParameterPresent>
                
</dynamic>
                order by Account_LastName
        
</select>

3、使用Hibernet方式
也就是使用Hibernet的数据库操作。

 
posted @ 2011-05-21 15:35 awp110 阅读(37) 评论(0) 编辑
 
 

最近这段时间一直在用IBatis来进行开发。现在把这段时间的一些开发心得写出来,跟大家一起分享。

首先,IBatis可以说转变了我以前的一些思想。我们以前开发项目的时候,总是先建立好数据库,分析清楚表与表之间的关系,才开始根据这些表进行实际的开发。实际上并没有完全把面向对象的优势展现出来。

现在的使用IBatis。顺序可以是这样,首先项目经理会分析整个项目,可以分成为几个对象,每个对象具有什么属性,什么方法。同时会用visio画出UML图来。这样可以说把面向对象的优势完全体现出来了。 然后程序员会根据UML图来操作对象,实现每个对象的属性和方法。这样就可以完全脱离数据库,去考虑程序的问题。这种开发方式特别适合于团队开发,团队中有专门的人去负责数据的建立,他可以根据项目中的Maps图清晰的知道每个数据表中的字段。

 
posted @ 2011-05-21 15:33 awp110 阅读(40) 评论(0) 编辑
 
 

iBatis中的动态查询还是比较好用的

如果想深入学习,可以参考 Manning.iBATIS.in.Action.Jan.2007

下面给出几个例子和dtd定义:

<select id="selectDispatchedKey" parameterClass="KeyAndKeyFlowInfo" resultMap="KeyAndKeyFlowResult">
   select distinct
    KEY_ID,
    USER_ID,
    INITIATOR,
    INIT_DATE,
    INITIATOR_EMAIL,
    SGS_KEY.BRANCH_NAME,
    APPROVER,
    APPROVER_EMAIL,
    APPROVE_DATE
   from
    SGS_KEY,
    SGS_KEY_FLOW
   where SGS_KEY.APPLY_FLOW_ID=SGS_KEY_FLOW.KEY_FLOW_ID
   <dynamic prepend="and" open="(" close=")">  
    <isNotNull property="ki.userId" prepend="and" removeFirstPrepend="false">
     USER_ID = #ki.userId#
    </isNotNull>
    <isNotNull property="kfi.initiator" prepend="and">
     INITIATOR = #kfi.initiator#
    </isNotNull>
    <isNotNull property="kfi.initDate" prepend="and">
     INIT_DATE = #kfi.initDate#
    </isNotNull>
   </dynamic>
</select>


<update id="updateKeyFlow" parameterClass="KeyFlowInfo">
   update SGS_KEY_FLOW set
   <dynamic>
     KEY_FLOW_ID =#keyFlowId#
    <isNotNull property="branchName" prepend=",">
     BRANCH_NAME = #branchName#
    </isNotNull>
    <isNotEqual property="operation" prepend="," compareProperty="operation" compareValue="0">
     OPERATION = #operation#
    </isNotEqual>
    <isNotNull property="initiator" prepend=",">
     INITIATOR = #initiator#
    </isNotNull>
    <isNotNull property="initiatorEmail" prepend=",">
     INITIATOR_EMAIL = #initiatorEmail#
    </isNotNull>
    <isNotNull property="initDate" prepend=",">
     INIT_DATE = #initDate#
    </isNotNull>
    <isNotNull property="approver" prepend=",">
     APPROVER = #approver#
    </isNotNull>
    <isNotNull property="approverEmail" prepend=",">
     APPROVER_EMAIL = #approverEmail#
    </isNotNull>
    <isNotNull property="approveDate" prepend=",">
     APPROVE_DATE = #approveDate#
    </isNotNull>
    <isNotEqual property="keyNum" prepend="," compareProperty="keyNum" compareValue="0">
     KEY_NUM = #keyNum#
    </isNotEqual>
    <isNotNull property="applyReason" prepend=",">
     APPLY_REASON = #applyReason#
    </isNotNull>
    <isNotNull property="rejectReason" prepend=",">
     REJECT_REASON = #rejectReason#
    </isNotNull>                       
   </dynamic>
   where
    KEY_FLOW_ID =#keyFlowId#
</update>

0:26 2008-2-20补充:

让iBatis中的insert返回主键 [ibatis insert 返回 自增主键]

iBatis SQL-map 文件书写注意事项

 

http://ibatis.apache.org/dtd/sql-map-2.dtd 中有<dynamic>的定义

<!-- - - - - - - - - - - - - - - - - - - - - - - - -
                 DYNAMIC ELEMENTS
- - - - - - - - - - - - - - - - - - - - - - - - -->

<!ELEMENT dynamic (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST dynamic
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
>

<!ELEMENT isNotNull (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNotNull
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isNull (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNull
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isNotPropertyAvailable (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNotPropertyAvailable
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #REQUIRED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isPropertyAvailable (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isPropertyAvailable
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #REQUIRED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isEqual (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isEqual
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isNotEqual (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNotEqual
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isGreaterThan (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isGreaterThan
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isGreaterEqual (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isGreaterEqual
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isLessThan (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isLessThan
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isLessEqual (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isLessEqual
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
compareProperty CDATA #IMPLIED
compareValue CDATA #IMPLIED
>

<!ELEMENT isEmpty (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isEmpty
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isNotEmpty (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNotEmpty
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isParameterPresent (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isParameterPresent
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT isNotParameterPresent (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST isNotParameterPresent
prepend CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
removeFirstPrepend (true|false) #IMPLIED
>

<!ELEMENT iterate (#PCDATA | include | iterate | isParameterPresent | isNotParameterPresent | isEmpty | isNotEmpty | isNotNull | isNull | isNotEqual | isEqual | isGreaterThan | isGreaterEqual | isLessThan | isLessEqual | isPropertyAvailable | isNotPropertyAvailable)*>
<!ATTLIST iterate
prepend CDATA #IMPLIED
property CDATA #IMPLIED
removeFirstPrepend (true|false|iterate) #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
conjunction CDATA #IMPLIED
>

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/alex197963/archive/2008/11/27/3391551.aspx

 
posted @ 2011-05-21 15:22 awp110 阅读(85) 评论(0) 编辑
 
 

iBatis解决sql注入

(1) ibatis xml配置:下面的写法只是简单的转义 name like '%$name$%'

(2) 这时会导致sql注入问题,比如参数name传进一个单引号“'”,生成的sql语句会是:name like '%'%'

(3) 解决方法是利用字符串连接的方式来构成sql语句 name like '%'||'#name#'||'%'

(4) 这样参数都会经过预编译,就不会发生sql注入问题了。

(5) #与$区别:

#xxx# 代表xxx是属性值,map里面的key或者是你的pojo对象里面的属性, ibatis会自动在它的外面加上引号,表现在sql语句是这样的 where xxx = 'xxx' ;

$xxx$ 则是把xxx作为字符串拼接到你的sql语句中, 比如 order by topicId , 语句这样写 ... order by #xxx# ibatis 就会把他翻译成 order by 'topicId' (这样就会报错) 语句这样写 ... order by $xxx$ ibatis 就会把他翻译成 order by topicId

posted @ 2011-12-09 15:29  小丫丫宝贝  阅读(899)  评论(0编辑  收藏  举报