一、   存储过程简介

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程序集中是作为类的公共静态方法实现的。(本文就不作介绍了)

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

  1. --创建测试books
  2. create table books (
  3. book_id int identity(1,1) primary key,
  4. book_name varchar(20),
  5. book_price float,
  6. book_auth varchar(10)
  7. );
  8. --插入测试数据
  9. insert into books (book_name,book_price,book_auth)
  10. values
  11. ('论语',25.6,'孔子'),
  12. ('天龙八部',25.6,'金庸'),
  13. ('雪山飞狐',32.7,'金庸'),
  14. ('平凡的世界',35.8,'路遥'),
  15. ('史记',54.8,'司马迁');

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

sqlserver 创建存储过程:

  1. if (exists (select * from sys.objects where name = 'getAllBooks'))--判断是否存在存储过程
  2. drop proc getAllBooks -- 删除
  3. go
  4. create procedure getAllBooks(@rowcount INT OUTPUT) -- 创建存储过程
  5. as
  6. begin
  7. select * from books;
  8. SET @rowcount=@@rowcount
  9. end;
  10. go
  11. --调用,执行存储过程
  12. DECLARE @count INT
  13. EXECUTE getAllBooks @count OUTPUT
  14. PRINT @count

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

  1. public String StorageInfo(){
  2. String param2Value = (String) jdbcTemplate.execute(
  3. new CallableStatementCreator() {
  4. public CallableStatement createCallableStatement(Connection con) throws SQLException {
  5. String storedProc = "{CALL getAllBooks(?)}";// 调用的sql
  6. CallableStatement cs = con.prepareCall(storedProc);
  7. // cs.setInt(1, 2);// 设置输入参数的值
  8. // cs.setString(2, "99");// 设置输入参数的值
  9. cs.registerOutParameter(1, Types.JAVA_OBJECT);// 注册输出参数的类型
  10. return cs;
  11. }
  12. }, new CallableStatementCallback() {
  13. public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
  14. cs.execute();
  15. //ResultSet rs = (ResultSet) cs.getObject(2);// 获取游标一行的值
  16. ResultSet rs = cs.getResultSet();
  17. // System.out.println("CallableStatementCallback-------------:"+cs);
  18. while(rs.next()){
  19. int id = rs.getInt(1);
  20. String book_name = rs.getString(2);
  21. String book_auth = rs.getString(4);
  22. System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);
  23. }
  24. return null;// 获取输出参数的值
  25. }
  26. });
  27. System.out.println("天天-------------:"+param2Value);
  28. return param2Value;
  29. }

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

sqlserver 创建有参存储过程

  1. --2.创建有参存储过程
  2. if exists(select * from sysobjects where name='getAllBooks')
  3. drop proc getAllBooks
  4. go
  5. --创建存储过程输入参数。
  6. create proc getAllBooks
  7. @startId varchar(50)
  8. as
  9. begin
  10. (select * from books where book_id =@startId);
  11. end
  12. go
  13. declare @back varchar(2000)
  14. exec getAllBooks 2

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

  1. public String StorageInfo(){
  2. String param2Value = (String) jdbcTemplate.execute(
  3. new CallableStatementCreator() {
  4. public CallableStatement createCallableStatement(Connection con) throws SQLException {
  5. String storedProc = "{CALL getAllBooks(?)}";// 调用的sql
  6. CallableStatement cs = con.prepareCall(storedProc);
  7. cs.setInt(1, 2);// 设置输入参数的值
  8. // cs.setString(2, "99");// 设置输入参数的值
  9. // cs.registerOutParameter(2, Types.JAVA_OBJECT);// 注册输出参数的类型
  10. return cs;
  11. }
  12. }, new CallableStatementCallback() {
  13. public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
  14. cs.execute();
  15. //ResultSet rs = (ResultSet) cs.getObject(2);// 获取游标一行的值
  16. ResultSet rs = cs.getResultSet();
  17. // System.out.println("CallableStatementCallback-------------:"+cs);
  18. while(rs.next()){
  19. int id = rs.getInt(1);
  20. String book_name = rs.getString(2);
  21. String book_auth = rs.getString(4);
  22. System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);
  23. }
  24. return null;// 获取输出参数的值
  25. }
  26. });
  27. System.out.println("天天-------------:"+param2Value);
  28. return param2Value;
  29. }

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

  1. --2.创建有参存储过程
  2. if exists(select * from sysobjects where name='getAllBooks')
  3. drop proc getAllBooks
  4. go
  5. --创建存储过程输入参数。
  6. create proc getAllBooks
  7. @startId varchar(50),
  8. @data nvarchar(100) output
  9. as
  10. begin
  11. set @data = (select book_name from books where book_id =@startId);
  12.  
  13. end
  14. go
  15. declare @back varchar(2000)
  16. exec getAllBooks 2 ,@back output
  17. select @back dat

java 实现代码

  1. public String StorageInfo(){
  2. String param2Value = (String) jdbcTemplate.execute(
  3. new CallableStatementCreator() {
  4. public CallableStatement createCallableStatement(Connection con) throws SQLException {
  5. String storedProc = "{CALL getAllBooks(?,?)}";// 调用的sql
  6. CallableStatement cs = con.prepareCall(storedProc);
  7. cs.setInt(1, 2);// 设置输入参数的值
  8. // cs.setString(2, "99");// 设置输入参数的值
  9. cs.registerOutParameter(2, Types.VARCHAR);// 注册输出参数的类型
  10. return cs;
  11. }
  12. }, new CallableStatementCallback() {
  13. public Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
  14. cs.execute();
  15. //ResultSet rs = (ResultSet) cs.getObject(2);// 获取游标一行的值
  16. ResultSet rs = cs.getResultSet();
  17. System.out.println("CallableStatementCallback-------------:"+cs.getString(2));
  18. // while(rs.next()){
  19. // int id = rs.getInt(1);
  20. // String book_name = rs.getString(2);
  21. // String book_auth = rs.getString(4);
  22. // System.out.println("id:"+id+" 书名:"+book_name+" 作者:"+book_auth);
  23. // }
  24. return null;// 获取输出参数的值
  25. }
  26. });
  27. System.out.println("天天-------------:"+param2Value);
  28. return param2Value;
  29. }

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

这是我的代码

  1. --2.创建有参存储过程 游标接收结果集
  2.  
  3. if exists(select * from sysobjects where name='proc_find_stu')
  4. drop proc proc_find_stu
  5. go
  6. --创建存储过程输入参数。
  7. create proc proc_find_stu
  8. @startId varchar(50),
  9. @data CURSOR VARYING OUTPUT
  10. as
  11. begin
  12. -- SET NOCOUNT ON added to prevent extra result sets from
  13. -- interfering with SELECT statements.
  14. SET NOCOUNT ON;
  15. -- print @overTimeHour;
  16. -- 1. 声明游标: DECLARE CURSOR_PriceChangeRecord
  17. SET @data = CURSOR
  18. FORWARD_ONLY STATIC
  19. FOR (select * from books where book_id =@startId);
  20. OPEN @data
  21. return 1;
  22. end
  23. go
  24. declare @back CURSOR
  25. exec proc_find_stu 2,@back;
  26. 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

SQLServer 的存储过程与java交互的更多相关文章

  1. Flex使用Blazeds与Java交互及自定义对象转换详解-DATAGRID读取ORACLE数据

    http://www.cnblogs.com/RocD-DuPeng/articles/1751040.html 一.建立Flex与Java交互的工程. 本文中讲到的交互是利用Blazeds的,因为这 ...

  2. Sqlserver中存储过程,触发器,自定义函数(二)

    Sqlserver中存储过程,触发器,自定义函数: 自定义函数:1.函数类型:2.函数的参数和返回值: 1.函数类型:标量值函数,返回的是一个标量值表值函数:内联表值函数:多语句表值函数. 标量值函数 ...

  3. Sqlserver中存储过程,触发器,自定义函数(一)

    Sqlserver中存储过程,触发器,自定义函数 1.存储过程有关内容存储过程的定义:存储过程的分类:存储过程的创建,修改,执行:存储过程中参数的传递,返回与接收:存储过程的返回值:存储过程使用游标. ...

  4. Sqlserver中存储过程,触发器,自定义函数

    Sqlserver中存储过程,触发器,自定义函数: 1. 触发器:是针对数据库表或数据库的特殊存储过程,在某些行为发生的时候就会被激活 触发器的分类: DML触发器:发生在数据操作语言执行时触发执行的 ...

  5. JSON序列化及利用SqlServer系统存储过程sp_send_dbmail发送邮件(一)

    JSON序列化 http://www.cnblogs.com/yubaolee/p/json_serialize.html 利用SqlServer系统存储过程sp_send_dbmail发送邮件(一) ...

  6. SqlServer中存储过程中将Exec的执行结果赋值给变量输出

    原文 SqlServer中存储过程中将Exec的执行结果赋值给变量输出 背景: 遇到这样一种情况:动态向存储过程中传入表名和表的某些属性(这里用到的是主键ID),然后利用这两个变量查出一条数据的某些字 ...

  7. atitit.js 与c# java交互html5化的原理与总结.doc

    atitit.js 与c# java交互html5化的原理与总结.doc 1. 实现html5化界面的要解决的策略1 1.1. Js交互1 1.2. 动态参数个数1 1.3. 事件监听2 2. sen ...

  8. SqlServer复杂存储过程

    SqlServer复杂存储过程 CREATE PROCEDURE FETCH_GOOUT_INFO AS BEGIN WITH l as(SELECT A.ZJHM, O.KSQR, O.JSRQ, ...

  9. SqlServer中存储过程 returnC#代码处理以及对应的MySQL如何改写

    一.SqlServer 中 1. 创建表 create table testuser( id int, --primary key, names ), address ), paw ) ) 2.创建存 ...

随机推荐

  1. python初步要点

    [python初步要点] #! 用于告诉操作系统去哪里找Python解释器为运行您的程序. 1.print 的输出有以下2种形式,""%()的形式类似于C的printf. 要注意逗 ...

  2. Thrift 实现 JAVA,PHP,C#,Python 互相通信

    Thrift介绍   https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/index.html 首先需要下载 Thrift.exe ...

  3. PLSQL Developer工具的使用

    1.运行 2.字体大小 导入csv文件. 任何工具都有失败的可能, 这个时候,也失败了. 当然还有另一种可能,文件被人为改过了,括号改过了,就即使使用下面的kettle工具也没用了.这时可以导出文件对 ...

  4. strip命令

    去掉文件里调试和符号信息,文件大小变小,一般在发布的时候使用. 主要作用于可执行文件,动态库,目标文件等. 可参考:http://blog.csdn.net/stpeace/article/detai ...

  5. 为什么数组没有实现Iterable接口,但可以使用foreach语句遍历

    在Java中,对于数组为什么能够使用foreach语句一直感觉很困惑. 对于能够使用foreach语句进行遍历的对象,只有两种情况,其中一种是遍历对象必须实现Iterable接口,实现ierator( ...

  6. Java JarFile 解析

    Java JarFile 解析 package com.github.binarylei; import java.io.*; import java.net.URL; import java.net ...

  7. c#解决Nullable类型的转换 (包含DataContract的序列化和反序列化以及 该例子应用在反射属性setvalue的时候有用)

    using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflect ...

  8. 为什么不加WWW的域名能访问,前面加了WWW后不能访问?

    解决方法:我的主机记录没有添加www,添加后就可以访问了

  9. wCF 问题收集页

    1.设置最大序列化集合元素个数 http://msdn.microsoft.com/zh-cn/library/system.runtime.serialization.datacontractser ...

  10. arean.c

    glibc-2.14中的arean.c源代码,供研究malloc和free实现使用: /* Malloc implementation for multiple threads without loc ...