前言:这篇整理好已经很久了,一直觉得还有很多需要改进的地方,然后拖着等待完善,nnd,竟然等成跨年贴了。现在把它发布一下,将来自己可能会用到,同时希望对您有帮助。
一、存储过程分类
这里我们把存储过程大致分为两类:查询类型的存储过程和更新类型的存储过程。
1、查询类型的存储过程
可以直接理解为Select查询类型的存储过程,其实任何不曾改变数据库数据的存储过程都可以归为这一类。
2、更新类型的存储过程
这一类型的您可以直接理解为Insert,Update和Delete类型的存储过程,其实任何改变数据库数据的存储过程都可以归为这一类。
ps:上面这两点纯粹是个人片面理解,难免有不严谨的地方,高手54.
二、iBATIS.net调用存储过程
我们继续前面几篇介绍的iBATIS.Net的例子,简单改进为调用存储过程的方法。
1、查询类型
创建一个存储过程,这个存储过程按照名字模糊查找特定的记录:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
GO
CREATE PROCEDURE [ dbo ] . [ ProcGetPersonByName ]
@FirstName varchar ( 50 ),
@LastName varchar ( 50 )
AS
BEGIN
SELECT
PER_ID Id,
PER_FIRST_NAME FirstName,
PER_Last_NAME LastName,
PER_BIRTH_DATE BirthDate,
PER_WEIGHT_KG Weight,
PER_HEIGHT_M Height
FROM
Person (NOLOCK)
WHERE
PER_FIRST_NAME LIKE ' % ' + @FirstName + ' % '
OR
PER_Last_NAME LIKE ' % ' + @LastName + ' % '
END
然后,我们在xml的statements节点中配置如下:
< procedure id ="SelectByName" parameterMap ="ParamSelect" resultMap ="ResultPerson" >
ProcGetPersonByName
</ procedure >
需要说明的是,在procedure节点中,parameterMap和resultMap也需要我们配置,其中resultMap和之前的配置方式一致,主要是匹配存储过程返回的结果列:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< resultMap id ="ResultPerson" class ="TestPerson" >
< result property ="Id" column ="Id" />
< result property ="FirstName" column ="FirstName" />
< result property ="LastName" column ="LastName" />
< result property ="BirthDate" column ="BirthDate" />
< result property ="WeightInKilograms" column ="Weight" />
< result property ="HeightInMeters" column ="Height" />
</ resultMap >
</ resultMaps >
接着就是parameterMap配置,
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
<!-- 注意:parameterMap中的参数个数和顺序要和ProcGetPersonByName存储过程中的一致 -->
< parameterMap id ="ParamSelect" class ="TestPerson" >
< parameter property ="FirstName" />
< parameter property ="LastName" />
</ parameterMap >
</ parameterMaps >
必须注意:parameterMap中的参数个数和顺序要和存储过程中的一致。
在cs中调用如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
IList < TestPerson > list = SqlMap.QueryForList < TestPerson > ( " SelectByName " , model);
return list;
}
2、更新类型
a、插入
插入成功之后,我们要返回一条记录对应的自增长的主键,存储过程如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
GO
CREATE PROCEDURE [ dbo ] . [ ProcInsertPerson ]
@FirstName varchar ( 50 ),
@LastName varchar ( 50 ),
@BirthDate datetime ,
@Weight float ,
@Height float
AS
BEGIN
INSERT Person
(
PER_FIRST_NAME,
PER_Last_NAME,
PER_BIRTH_DATE,
PER_WEIGHT_KG,
PER_HEIGHT_M
)
VALUES
(
@FirstName ,
@LastName ,
@BirthDate ,
@Weight ,
@Height
)
-- 返回最新主键
SELECT @@IDENTITY AS value
END
在cs中调用如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
int id = 0 ;
object obj = SqlMap.Insert( " InsertPersonByProc " , person);
SqlMap.BeginTransaction();
SqlMap.CommitTransaction();
if (obj != null )
{
id = int .Parse(obj.ToString());
}
return id;
}
需要注意的是,插入类型的存储过程必须有最后这一句 “SELECT @@IDENTITY AS value”。您可以将它注释掉测试一下。在笔者这里测试的结果就是,在Insert,Update和Delete类型的存储过程中,如果没有output参数或者其他特定返回值的处理,SqlMap的Insert,Update和Delete执行存储过程的返回值始终是-1.
b、更新
正像我们测试的那样,执行Update类型的存储过程默认的返回值始终是-1,对于程序员而言,字面理解也就是存储过程所影响的数据表的行数是-1,这显然与我们实际的影响不一致(根据id更新,所以这个存储过程执行一次影响1条或者是0条记录),所以它返回的值至少应该不是-1。我们可以改进的方法是每执行一次更新存储过程,让它返回一个整数值比如1,表示存储过程执行成功。
so,改进后的存储过程如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
GO
CREATE PROCEDURE [ dbo ] . [ ProcUpdatePersonAndOutput ]
@ReturnValue int output,
@Id int ,
@FirstName varchar ( 50 ),
@LastName varchar ( 50 )
AS
BEGIN
UPDATE Person
SET
PER_FIRST_NAME = @FirstName ,
PER_Last_NAME = @LastName
WHERE PER_ID = @Id
SET @ReturnValue = 1
END
在调用这个存储过程的时候,如果要取得返回值,一种合理的写法是下面这样的:
在xml文件的statements节点中配置如下:
< procedure id ="UpdatePersonByProcAndOutput" parameterMap ="ParamUpdateOutput" >
ProcUpdatePersonAndOutput
</ procedure >
parameterMap的配置:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< parameterMap id ="ParamUpdateOutput" class ="IDictionary" >
< parameter property ="ReturnValue" column ="ReturnValue" direction ="Output" />
< parameter property ="Id" />
< parameter property ="FirstName" />
< parameter property ="LastName" />
</ parameterMap >
cs文件中调用如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/// 存储过程的output 参数返回
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
public int UpdateAndGetOutput(TestPerson person)
{
int i = 0 ;
Hashtable htParam = new Hashtable();
htParam[ " ReturnValue " ] = i;
htParam[ " Id " ] = person.Id;
htParam[ " FirstName " ] = person.FirstName;
htParam[ " LastName " ] = person.LastName;
i = SqlMap.Update( " UpdatePersonByProcAndOutput " , htParam); // 这里还是-1
i = ( int )htParam[ " ReturnValue " ]; // 这里才是最后的返回值
return i; // 返回值1
}
ps:在cs中获取返回值我已经注释的很清楚,对于output参数,网上已经有人提出取不到返回值,解决的方法您可以参考这一篇和这一篇。本文的示例测试已经通过。
c、删除
删除是按照id删除一条记录,但是这里的示例我们还是按照类的方式作为传值参数,您可以把参数改成整数的形式。示例代码我就不贴了,文章最下面我会提供demo下载,所有的存储过程都在IBatisNetDemo的Script文件夹下。
ps:近一阶段忙于项目,iBatis.net的学习也搁置了。现在回看这篇真是木有技术,竟然花了这么长时间,杯具鸟啊。
demo下载地址:demo