java存储过程与sql语句_SQLServer 的存储过程与java交互

一、存储过程简介

Sql Server的存储过程是一个被命名的存储在服务器上的Transacation-Sql语句集合,是封装重复性工作的一种方法,它支持用户声明的变量、条件执行和其他强大的编程功能。

存储过程相对于其他的数据库访问方法有以下的优点:

(1)重复使用。存储过程可以重复使用,从而可以减少数据库开发人员的工作量。

(2)提高性能。存储过程在创建的时候就进行了编译,将来使用的时候不用再重新编译。一般的SQL语句每执行一次就需要编译一次,所以使用存储过程提高了效率。

(3)减少网络流量。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。

(4)安全性。参数化的存储过程可以防止SQL注入式的攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。

存储过程一共分为了三类:用户定义的存储过程、扩展存储过程以及系统存储过程。

其中,用户定义的存储过程又分为Transaction-SQL和CLR两种类型。

Transaction-SQL 存储过程是指保存的Transaction-SQL语句集合,可以接受和返回用户提供的参数。

CLR存储过程是指对.Net Framework公共语言运行时(CLR)方法的引用,可以接受和返回用户提供的参数。他们在.Net Framework程序集中是作为类的公共静态方法实现的。(本文就不作介绍了)

二、先建一个测试用的表  (很基础的代码有点基础是可以看懂的)

--创建测试books表

create table books (

book_idint identity(1,1) primary key,

book_name varchar(20),

book_pricefloat,

book_auth varchar(10)

);--插入测试数据

insert into books (book_name,book_price,book_auth)

values

('论语',25.6,'孔子'),

('天龙八部',25.6,'金庸'),

('雪山飞狐',32.7,'金庸'),

('平凡的世界',35.8,'路遥'),

('史记',54.8,'司马迁');

三、创建无参存储过程有写返回参数(返回结果集,至于为什么不使用游标返回,而是直接返回下面有介绍)

sqlserver 创建存储过程:

if (exists (select * from sys.objects where name = 'getAllBooks'))--判断是否存在存储过程

drop proc getAllBooks -- 删除

go

create procedure getAllBooks(@rowcount INT OUTPUT) -- 创建存储过程

as

begin

select*from books;

SET @rowcount=@@rowcount

end;

go--调用,执行存储过程

DECLARE @count INT

EXECUTE getAllBooks @count OUTPUT

PRINT @count

java 代码(使用jdbcTemplate获取结果集)

publicString StorageInfo(){

String param2Value=(String) jdbcTemplate.execute(newCallableStatementCreator() {public CallableStatement createCallableStatement(Connection con) throwsSQLException {

String storedProc= "{CALL getAllBooks(?)}";//调用的sql

CallableStatement cs =con.prepareCall(storedProc);//cs.setInt(1, 2);//设置输入参数的值//cs.setString(2, "99");//设置输入参数的值

cs.registerOutParameter(1, Types.JAVA_OBJECT);//注册输出参数的类型

returncs;

}

},newCallableStatementCallback() {public Object doInCallableStatement(CallableStatement cs) throwsSQLException, DataAccessException {

cs.execute();//ResultSet rs = (ResultSet) cs.getObject(2);//获取游标一行的值

ResultSet rs =cs.getResultSet();//System.out.println("CallableStatementCallback-------------:"+cs);

while(rs.next()){int id = rs.getInt(1);

String book_name= rs.getString(2);

String book_auth= rs.getString(4);

System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);

}return null;//获取输出参数的值

}

});

System.out.println("天天-------------:"+param2Value);returnparam2Value;

}

四、创建有参存储过程没写返回参数(注意其中参数的区别,这个没有写返回out,sqlserver会自动返回)

sqlserver 创建有参存储过程

--2.创建有参存储过程if exists(select * from sysobjects where name='getAllBooks')

drop proc getAllBooks

go--创建存储过程输入参数。

create proc getAllBooks

@startId varchar(50)

as

begin

(select* from books where book_id =@startId);

end

go

declare @back varchar(2000)

exec getAllBooks2

java 实现  注意:没有注册输出参数

publicString StorageInfo(){

String param2Value=(String) jdbcTemplate.execute(newCallableStatementCreator() {public CallableStatement createCallableStatement(Connection con) throwsSQLException {

String storedProc= "{CALL getAllBooks(?)}";//调用的sql

CallableStatement cs =con.prepareCall(storedProc);

cs.setInt(1, 2);//设置输入参数的值//cs.setString(2, "99");//设置输入参数的值//cs.registerOutParameter(2, Types.JAVA_OBJECT);//注册输出参数的类型

returncs;

}

},newCallableStatementCallback() {public Object doInCallableStatement(CallableStatement cs) throwsSQLException, DataAccessException {

cs.execute();//ResultSet rs = (ResultSet) cs.getObject(2);//获取游标一行的值

ResultSet rs =cs.getResultSet();//System.out.println("CallableStatementCallback-------------:"+cs);

while(rs.next()){int id = rs.getInt(1);

String book_name= rs.getString(2);

String book_auth= rs.getString(4);

System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);

}return null;//获取输出参数的值

}

});

System.out.println("天天-------------:"+param2Value);returnparam2Value;

}

五、创建有参返回单个属性值

--2.创建有参存储过程if exists(select * from sysobjects where name='getAllBooks')

drop proc getAllBooks

go--创建存储过程输入参数。

create proc getAllBooks

@startId varchar(50),

@data nvarchar(100) output

as

begin

set @data= (select book_name from books where book_id =@startId);

end

go

declare @back varchar(2000)

exec getAllBooks2,@back output

select @back dat

java 实现代码

publicString StorageInfo(){

String param2Value=(String) jdbcTemplate.execute(newCallableStatementCreator() {public CallableStatement createCallableStatement(Connection con) throwsSQLException {

String storedProc= "{CALL getAllBooks(?,?)}";//调用的sql

CallableStatement cs =con.prepareCall(storedProc);

cs.setInt(1, 2);//设置输入参数的值//cs.setString(2, "99");//设置输入参数的值

cs.registerOutParameter(2, Types.VARCHAR);//注册输出参数的类型

returncs;

}

},newCallableStatementCallback() {public Object doInCallableStatement(CallableStatement cs) throwsSQLException, DataAccessException {

cs.execute();//ResultSet rs = (ResultSet) cs.getObject(2);//获取游标一行的值

ResultSet rs =cs.getResultSet();

System.out.println("CallableStatementCallback-------------:"+cs.getString(2));//while(rs.next()){//int id = rs.getInt(1);//String book_name = rs.getString(2);//String book_auth = rs.getString(4);//System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);//}

return null;//获取输出参数的值

}

});

System.out.println("天天-------------:"+param2Value);returnparam2Value;

}

六、下面来说说sqlserver 为什么不能返回游标

这是我的代码

--2.创建有参存储过程 游标接收结果集if exists(select * from sysobjects where name='proc_find_stu')

drop proc proc_find_stu

go--创建存储过程输入参数。

create proc proc_find_stu

@startId varchar(50),

@data CURSOR VARYING OUTPUT

as

begin--SET NOCOUNT ON added to prevent extra result sets from--interfering with SELECT statements.

SET NOCOUNT ON;--print @overTimeHour;-- 1. 声明游标: DECLARE CURSOR_PriceChangeRecord

SET @data=CURSOR

FORWARD_ONLY STATIC

FOR (select* from books where book_id =@startId);

OPEN @datareturn 1;

end

go

declare @back CURSOR

exec proc_find_stu2,@back;

select @back data

最开始头儿给了一个在oracle上可以直接跑的存储过程,在java程序里直接用jdbc来调用非常方便,没什么问题。之后便是狂找资料,把oracle上用PL/SQL写的存储过程改写成sqlserver上用Transact-SQL写的存储过程,改阿改,终于,在sqlserver的查询分析器上可以执行了,本以为已经做到这一步了,在jdbc里直接调用还不是小菜一碟,没想到呀,问题来了。

头儿给的这个存储过程有一个输出参数,是cursor(游标)型的,在jdbc里要用registerOutParameter这个方法在执行存储过程之前注册一下输出参数类型,

对于oracle的jdbc驱动这个问题很好办。直接写registerOutParameter(1,

oracle.jdbc.OracleTypes.CURSOR);就OK了,看到oracle.jdbc.OracleTypes.CURSOR了吧,oracle正不错,直接就给你提供了一个表示游标型的整型常量。整个调用过程如下:

CallableStatement proc = null;

proc = conn.prepareCall("{call PROC_FWMA_DATAARCHIVE}");

proc.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR);

proc.execute();

可microsoft呢,根本没有在它的jdbc驱动里提供类似的这样一个整型常量(尽管sqlserver里的存储过程是支持游标类型的输出参数的),用标准jdbc里的数据类型Types.other也没有,以运行,就抛异常了,什么mircosoft

jdbc

不支持这样的数据类型。咋办呢,到处查资料呀,各种试建立临时表等等之类然而却没啥用,最后想看看有没有前辈碰到过这样的问题,可是怎么碰到这种问题的人似乎很少呢,找了半天,终于看到外国人的论坛上有人提出和我一样的问题,结果答案很打击人,说是microsoft的jdbc就是不支持。可是这个输出的结果是很重要的,那有没有其他解决方法呢?再查sqlserver的联机帮助,看到sqlserver的存储过程还支持直接返回结果集,只要在存储过程里写select就可以了,哈哈,些个简单的存储过程,没有输出参数。再修改刚才段代码为

CallableStatement proc = null;

proc = conn.prepareCall("{call PROC_FWMA_DATAARCHIVE}");

ResultSet rs = null;

rs = proc.getResultSet();

很好,结果集就拿到了。问题解决了?NO,试验用的是简单的存储过程,再用正式的那个复杂的带事务操作的存储过程,jdbc又傻了,如果执行存储过程用的是execute(),它就只返回出结果集,而不能做存储过程中的insert

delete操作,如果用executeUpdate()执行,结果集就返回不出来了。

在这个问题上整整卡了一天,后来不知道怎么想了想,突然发现在存储过程中,我把要返回结果集的那句select是放在了事务操作的外面,会不会是这里有问题呢?马上动手,把select塞到事务里面,再运行,终于OK了。

猜测可能是如果将select放在事务外的话,它和事务是同级别的,如果用execute()执行的话,就只做了select,不做事务了。当然这只是猜测了,真的要弄明白,恐怕要写email到微软去问了,英语太差,就不丢人现眼了。哪位大牛帮忙问问?

示例代码:https://gitee.com/xdymemory00/sqlserver-CunChuGuoChengYujavaJiaoHu.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值