iBatis.Net实现返回DataTable和DataSet对象

 

如题。要返回一个ADO.NET对象好像没有使用ORM的必要,而且从编程的角度看这样的实现一点也不OO,但是实际的开发场景中还是会碰到这种需求的。下面我就借鉴前人的经验,结合实际的示例,再总结一下。如果您认真看完,应该可以体会得到我的一些尝试,而不是人云亦云的照搬代码。

 

1、获得DbCommand对象

对于SQL语句,方法如下:

GetDbCommand

对于存储过程,因为对于参数类型的不同,需要多几步处理(因为需要多维护一个参数字典和其对应的ParameterDirection字典):

        /// <summary>
        /// 获取DbCommand,主要是针对存储过程
        /// </summary>
        /// <param name="sqlMapper"></param>
        /// <param name="statementName"></param>
        /// <param name="paramObject">参数</param>
        /// <param name="dictParam">参数字段</param>
        /// <param name="dictParmDirection">ParameterDirection字典</param>
        /// <param name="cmdType"></param>
        /// <returns></returns>
        protected virtual IDbCommand GetDbCommand(ISqlMapper sqlMapper, string statementName, object paramObject, IDictionary dictParam, IDictionary<string, ParameterDirection> dictParmDirection, CommandType cmdType)
        {
            if (cmdType == CommandType.Text)
            {
                return GetDbCommand(sqlMapper, statementName, paramObject);
            }

            IStatement statement = sqlMapper.GetMappedStatement(statementName).Statement;
            IMappedStatement mapStatement = sqlMapper.GetMappedStatement(statementName);
            ISqlMapSession session = new SqlMapSession(sqlMapper);

            if (sqlMapper.LocalSession != null)
            {
                session = sqlMapper.LocalSession;
            }
            else
            {
                session = sqlMapper.OpenConnection();
            }

            RequestScope request = statement.Sql.GetRequestScope(mapStatement, paramObject, session);
            mapStatement.PreparedCommand.Create(request, session as ISqlMapSession, statement, paramObject);
            IDbCommand cmd = session.CreateCommand(cmdType);
            cmd.CommandText = request.IDbCommand.CommandText;
            if (cmdType != CommandType.StoredProcedure || dictParam == null)
            {
                return cmd;
            }
            foreach (DictionaryEntry de in dictParam) //存储过程 
            {
                string key = de.Key.ToString();
                IDbDataParameter dbParam = cmd.CreateParameter();
                dbParam.ParameterName = key;
                dbParam.Value = de.Value;

                if (dictParmDirection != null && dictParmDirection.ContainsKey(key))
                {
                    dbParam.Direction = dictParmDirection[key]; //ParameterDirection
                }
                cmd.Parameters.Add(dbParam);
            }
            return cmd;
        }

代码写得可能还有改进的必要,有需要从事这方面开发的童鞋,如果您看着有更好的办法请不吝赐教。

备注:

a、对于1.6.1之前的版本,获得命令的方式可以通过RequestScope的IDbCommand属性,但是1.6.1版本的IDbCommand属性返回的是IBatisNet.DataMapper.Commands.DbCommandDecorator对象,您可以注释代码验证一下。

b、网上有些文章贴的方法返回的DbCommand对象都是对于拼接SQL语句而言,没有实现获取存储过程的DbCommand(有参数无参数的都要考虑)。本文在原有资料的基础上,尝试着做出改进,目前支持SQL语句和存储过程。

 

2、返回DataSet对象

通过SQL语句,获取DataSet:

     public DataSet GetDSPerson(int id)
        {
            string sql = this.GetRuntimeSql(this.SqlMapper, this.GetStatementName("GetDSPerson"), id);
            return this.QueryForDataSet(this.SqlMapper, this.GetStatementName("GetDSPerson"), id);
        }

XML配置:

    <select id="GetDSPerson" parameterClass="int" resultClass="System.Data.DataSet">
      <include refid="CommonPersonColumns4Select"></include>
      WHERE 1=1 AND Id=$id$
    </select>

客户端的调用:

             int id = 1;
            DataSet ds = ServiceFactory.CreatePersonService().GetDSPerson(id);
            Console.WriteLine(ds.GetXml());

执行结果返回如下:

DataSet

 

3、返回DataTable对象

a、通过SQL语句

QueryForDataTable

这个相对简单,因为前面2中已经得到了DataSet,DataTable的提取就轻而易举了。

b、通过含OUTPUT参数的存储过程

这个地方主要就是改进后的GetDbCommand重载方法的使用,

      /// <summary>
        /// 查询返回DataTable,对于包括OUTPUT参数的存储过程同样适用
        /// </summary>
        /// <param name="sqlMapper"></param>
        /// <param name="statementName"></param>
        /// <param name="paramObject">参数</param>
        /// <param name="dictParam">参数字典</param>
        /// <param name="dictParamDirection">ParameterDirection字典</param>
        /// <param name="htOutPutParameter">返回的Output参数值哈希表</param>
        /// <returns></returns>
        protected virtual DataTable QueryForDataTable(ISqlMapper sqlMapper, string statementName, object paramObject, IDictionary dictParam, IDictionary<string, ParameterDirection> dictParamDirection, out Hashtable htOutPutParameter)
        {
            DataSet ds = new DataSet();
            bool isSessionLocal = false;
            ISqlMapSession session = sqlMapper.LocalSession;
            if (session == null)
            {
                session = new SqlMapSession(sqlMapper);
                session.OpenConnection();
                isSessionLocal = true;
            }

            IDbCommand cmd = GetDbCommand(sqlMapper, statementName, paramObject, dictParam, dictParamDirection, CommandType.StoredProcedure); //存储过程

            try
            {
                cmd.Connection = session.Connection;
                IDbDataAdapter adapter = session.CreateDataAdapter(cmd);
                adapter.Fill(ds);
            }
            finally
            {
                if (isSessionLocal)
                {
                    session.CloseConnection();
                }
            }
            htOutPutParameter = new Hashtable();
            foreach (IDataParameter parameter in cmd.Parameters)
            {
                if (parameter.Direction == ParameterDirection.Output)
                {
                    htOutPutParameter[parameter.ParameterName] = parameter.Value;
                }
            }
            return ds.Tables[0];
        }

测试的存储过程如下:

USE [TestDb]
GO
--根据id查询某人 并返回所有人中,最大体重,最小身高
CREATE    PROCEDURE [dbo].[usp_GetPersonById] 
    @MaxWeight float output,
    @MinHeight float output,
    @Id int
AS 
BEGIN
SELECT
	Id,
	FirstName,
	LastName,
	Weight,
	Height
FROM
	Person
	WHERE Id=@Id
	
SET @MaxWeight= (SELECT MAX(Weight) FROM Person)
SET @MinHeight= (SELECT MIN(Height) FROM Person)
END

本文的示例测试通过,返回的结果如下:

Query4Net

从上图中,我们可以看到最大体重是200,最矮身高是177。

 

4、小结和注意点

a、返回ADO.NET对象的方法,iBatis拼接的SQL语句必须通过$而不是熟悉的#符号连接,这个$会有众所周知的SQL注入的风险。

b、我还没有实际尝试过最新版本的iBatis,对于较新的版本,不知道本文的方法还适不适用。

c、我参考这篇文章的时候,发现说有一个无法返回output参数的问题。我尝试着重现这个问题。功夫不负有心人,本文示例中我已经实现好了,给自己鼓励一下。

 

参考:

http://blog.csdn.net/netjxz/archive/2007/07/02/1675430.aspx

http://mail-archives.apache.org/mod_mbox/ibatis-user-cs/200607.mbox/raw/%3ce1c5f9e0607191212w6aa9b60fi83b0243674a5a7bb@mail.gmail.com%3e

 

demo下载:IBatisNetApp

 

===============================分割线分割线===============================

    今天是西方传统节日圣诞节。早在几天前部门漂亮的细心的助理在装扮办公室的时候,我就已经感受到了良好的节日氛围。虽然对于任何不单独放假的中外各种节日,我一直都持着无所谓的态度。

    这个节日倒是让我想起大学里两个教过我的外教。我在大学里过的圣诞节就拜他们所赐,谁能想到平时安静沉默不很主动的我竟然也有过兴致勃勃地哼唱着欢快的圣诞歌曲的经历呢?当时我就读的学院外教非常少,我很幸运,被分进外教的班,上过两位外教的课。第一位外教M女士很漂亮,难得有淑女的气质,声音也很好听,我现在还记得她的微笑,倒是对她的授课方式没有半点印象。M女士教了我大一两个学期,然后过完圣诞节不久就离开了我们学校。另一位是个男外教K先生,他向我们自我介绍,最自豪的是自己的身高,说乔丹的身高就是他的身高,完了很单纯地开心大笑。K先生的授课方式比较闹腾,好像老有游戏和各种搞怪的惩罚,他教了我大二两个学期,然后好像也是在圣诞节过完不久离开了我们学校。大一的时候学校不允许提前考四级,美其名曰保证大家的学习积极性,那个时候大家学习相对认真开心和轻松一些。到了大二,经过学院某两个英语老师的反复提醒,四六级无形中会给大家带来压力,虽然很多人都已经很主动地准备着。很庆幸,上外教的课我们不会有这方面的烦恼。

    我还想起大学里的一个死党鑫爷。我们俩一起度过了大学里大部分的自习时间,并且开玩笑自称是xxx班泰山崩于前而不变色的仅存的两人。这个家伙曾经呆头呆脑一本正经地对我说圣诞节是用来纪念上帝诞辰的,我倒。在大二还发生了一件非常有趣的事情。有一天我和鑫爷上完自习,下楼梯碰到K先生,平时上英语课从来不主动的我硬着头皮上去打招呼,先hi后hello,眼神交流之后含羞微笑走人。倒是鑫爷有模有样地回头笑着不知向K先生说了什么。路上鑫爷还自夸说,你看,还是我牛吧,哈哈哈哈哈哈,点点点点点点。被我无情嘲弄一番,我还戏称他会说的所有外语我都可以给他列出来,什么哈罗啊,饭已OK,下来密西啦。鑫爷在大学里英语成绩从来都不好,但是他非常热爱学习英语,给培训机构贡献过不少银子。这个可爱的胖子在毕业后,继续攻读,硬是考上了研究生,让人刮目相看。

    圣诞节,让我怀念我曾经认识的一些乐观自信,友好真诚,努力踏实,热爱生活的人们。他们身上的一些可贵品质曾影响了我,并延续至今。

    淡淡一句Merry Christmas给他们送上祝福。

posted on 2010-12-25 21:59  JeffWong  阅读(6035)  评论(14编辑  收藏  举报