详细解释——理论知识:
对已储存过程的调用是CallableStatement对象所含的内容。
这种调用是用一种换码语法来写的,有两种形式 :一种形式带结果参,另一种形式不带结果参数。
结果参数是一种输出(OUT)参数,是已储存过程的返回值。
两种形式都可带有数量可变的输入(IN参数)、输出(OUT参数)或输入和输出(INOUT参数)的参数。
问号 将用作参数的占位符。
在JDBC中调用已储存过程的语法如下所示。
注意,方括号表示其间的内容是可选项;方括号本身并非语法的 组成部份。
{call过程名[(?,?,...)]} 返回结果参数的过程的语法为: {?=call过程名[(?,?,...)]} 不带参数的已储存过程的语法类似: {call过程名} 通常,创建CallableStatement对象的人应当知道所用的DBMS是支持已储存过程的,并且知道这些过程都是些 什么。
然而,如果需要检查,多种DatabaseMetaData方法都可以提供这样的信息。例如,如果DBMS支持已储 存过程的调用,则supportsStoredProcedures方法将返回true,而getProcedures方法将返回对已储存过程的 描述。 CallableStatement继承Statement的方法(它们用于处理一般的SQL语句),还继承了PreparedStatement的 方法(它们用于处理IN参)。
CallableStatement中定义的所有方法都用于处理OUT参数或INOUT参数的输出部分:注册OUT参数的JDBC类型 (一般SQL类型)、从这些参数中检索结果,或者检查所返回的值是否为JDBCNULL。
JDBC存储过程1、创建CallableStatement对象 CallableStatement对象是用Connection方法prepareCall创建的。
下例创建CallableStatement的实例,其中 含有对已储存过程getTestData调用。
该过程有两个变量,但不含结果参数:CallableStatementc stmt=con.prepareCall("{callgetTestData(?,?)}"); 其中?占位符为IN、OUT还是INOUT参数,取决于已储存过程getTestData。
JDBC存储过程2、IN和OUT参数 将IN参数传给CallableStatement对象是通过setXXX方法完成的。该方法继承自PreparedStatement。
所传入 参数的类型决定了所用的setXXX方法(例如,用setFloat来传入float值等)。
如果已储存过程返回OUT参数 ,则在执行CallableStatement对象以前必须先注册每个OUT参数的JDBC类型(这是必需的,因为某些DBMS要 求JDBC类型)。
注册JDBC类型是用registerOutParameter方法来完成的。
语句执行完后,CallableStatement 的getXXX方法将取回参数值。
正确的getXXX方法是为各参数所注册的JDBC类型所对应的Java类型。
换言之, registerOutParameter使用的是JDBC类型(因此它与数据库返回的JDBC类型匹配),而getXXX将之转换为 Java类型。
作为示例,下述代码先注册OUT参数,执行由cstmt所调用的已储存过程,然后检索在OUT参数中返回的值。
方 法getByte从第一个OUT参数中取出一个Java字节,而getBigDecimal从第二个OUT参数中取出一个BigDecimal 对象(小数点后面带三位数):
CallableStatementc stmt=con.prepareCall("{callgetTestData(?,?)}");
cstmt.registerOutParameter (1,java.sql.Types.TINYINT);
cstmt.registerOutParameter(2,java.sql.Types.DECIMAL,3);
cstmt.executeQuery(); byte x=cstmt.getByte(1);
java.math.BigDecimaln=cstmt.getBigDecimal(2,3);
CallableStatement与ResultSet不同,它不提供用增量方式检索大OUT值的特殊机制。
JDBC存储过程
3、INOUT参数 既支持输入又接受输出的参数(INOUT参数)除了调用registerOutParameter方法外,还要求调用适当的 setXXX方法(该方法是从PreparedStatement继承来的)。
setXXX方法将参数值设置为输入参数,而 registerOutParameter方法将它的JDBC类型注册为输出参数。
setXXX方法提供一个Java值,而驱动程序先把 这个值转换为JDBC值,然后将它送到数据库中。这种IN值的JDBC类型和提供给registerOutParameter方法的 JDBC类型应该相同。
然后,要检索输出值,就要用对应的getXXX方法。
例如,Java类型为byte的参数应该使 用方法setByte来赋输入值。
应该给registerOutParameter提供类型为TINYINT的JDBC类型,同时应使用 getByte来检索输出值。
下例假设有一个已储存过程reviseTotal,其唯一参数是INOUT参数。
方法setByte把此参数设为25,驱动程序 将把它作为JDBCTINYINT类型送到数据库中。
接着,registerOutParameter将该参数注册为JDBCTINYINT。
执 行完该已储存过程后,将返回一个新的JDBCTINYINT值。
方法getByte将把这个新值作为Javabyte类型检索。
CallableStatementc stmt=con.prepareCall("{callreviseTotal(?)}");
cstmt.setByte(1,25);
cstmt.registerOutParameter(1,java.sql.Types.TINYINT);
cstmt.executeUpdate(); byte x=cstmt.getByte(1); 1)返回一个结果集(ResultSet)。
2)返回一个特定的值。
下面来详细的说明。
1)返回一个结果集(ResultSet),这种类似通常的处理结果集
如果事先就有一个类似如下的
procedure CREATE PROCEDURE getShipQuantity
@jsid int AS SELECT jf_js_id,SUM(jf_ship_quantity)
AS
shipqty FROM tjobsheet_finish f
WHERE (jf_js_id=@jsid) GROUP BY jf_js_id
那么我们将通过如下的代码来调用
String sql = "{ call getShipQuantity(?) }";
Connection con = conn.connection();
ResultSet rs = null;
BigDecimal shipQuantity = new BigDecimal(0);
try{ CallableStatement cs = con.prepareCall(sql);
cs.setInt(1,jsoId);//设置输入参数
rs = cs.executeQuery();//返回结果集
if(rs.next()){
shipQuantity = new BigDecimal(rs.getDouble(2));
}
logger.debug("shipQuantity --------------------- "+shipQuantity);
}catch(Exception e){
logger.debug(e);
}
2)返回一个特定的值。
也就是说,在procedure的定义中已经用output输出参数了。
请看下面的
proceduer create procedure getSingleWgt
@@singleWgt numeric(8,3) output,@jsnum varchar(11) = '0000-0480'
as
declare @stwgt numeric(8,3)
select @stwgt = sum(b.stwgt) from js as a inner join jsactdtl as b on a.jsnum = b.jsnum
where a.completion = 1 and b.stflag = 22 and a.jsnum = @jsnum
select @@singleWgt = (@stwgt/orderedqty) from js where jsnum = @jsnum
那么我们将通过如下的代码来调用
String sql = "{ call getSingleWgt(?,?) }";
Connection con = getSession().connection();//得到connection
try{
CallableStatement cs = con.prepareCall(sql);//通过它来执行sql
cs.registerOutParameter(1,java.sql.Types.FLOAT);//注册输出参数
cs.setString(2,shipment.getJsnum());//指出输入参数
if(cs.execute()){//执行
float output = cs.getFloat(1);//返回值
}
}catch(Exception e){
logger.debug(e);
}
例子eg:创建存储过程
create or replace procedure p_find_emp( i_emp_id in tb_employee.emp_id%type ) is v_str_emp_name tb_employee.emp_name%type;
begin
select emp_name into v_str_emp_name from tb_employee where emp_id=i_emp_id;
dbms_output.put_line('该雇员名字叫:'||v_str_emp_name);
end p_find_emp; oracle 分页
--创建游标
create or replace package pkg_query is type cur_result_type is ref cursor;--游标的类型
end pkg_query;
create or replace procedure p_query_page( str_page_action in varchar
2, ---分页动作,可以使上一页:
previous_page,
下一页:next_page,
----首页:first_page,
尾页:last_page,
指定页:page_number
str_query_table in varchar2, -----需要查询的表,可以以单表或通过连接出来的虚拟表
str_query_condition in varchar2,-----分页的查询条件
str_result_col in varchar2,-----需要输出的字段名
str_order_condition in varchar2,-----查询的排序条件
str_order_style in varchar2,-----排序的风格,升序或降序
i_page_size in out number,-------每页显示的数据条数
i_current_page in out number, ----当前页索引
i_total_record out number,-----当前符合条件的总记录条数
i_total_page out number,-----当前符合条件的总页数
cur_query_result out pkg_query.cur_result_type------查询的结果 )
is v_str_query_sql varchar2(10000):='';-----查询的sql语句
v_i_start_record number(10):=0;----起始记录位置
v_i_end_record number(10):=0;----终止记录的位置
begin
-------检验指定需要查询的表的参数是否为空
if(str_query_table is null or str_query_table = '')
then raise_application_error(-20001,'需要查询的表不能为空');
end if;
v_str_query_sql:='select count(*) from '||str_query_table;
------当查询的条件不为空时,将相应的查询条件拼接到sql中
if(str_query_condition is not null and str_query_condition <> '')
then v_str_query_sql:=v_str_query_sql||'where '||str_query_condition;
end if;
-----PL/Sq 动态调用sql
execute immediate v_str_query_sql into i_total_record;
--------检测每页数据量,如果小于等于零,把每页数据量设为默认值10;
if ( i_page_size <= 0 )then i_page_size := 10;
end if;
------求当前符合条件的信息的总页数
if mod(i_total_record,i_page_size)=0 then i_total_page := (i_total_record/i_page_size);
else i_total_page := trunc(i_total_record/i_page_size)+1;
end if;
------根据当前的分页动作转换当前页索引
case str_page_action when 'first_page' then i_current_page := 1;
when 'last_page' then i_current_page := i_total_page;
when 'previous_page' then i_current_page := i_current_page-1;
when 'next_page' then i_current_page := i_current_page+1;
when 'page_number' then i_current_page := i_current_page;
else i_current_page := 1; end case;
------求起始记录的索引位置和终止记录的索引位置
v_i_start_record := (i_current_page-1)*i_page_size+1;
v_i_end_record := i_current_page*i_page_size;
-----根据以上结果拼接sql语句。
v_str_query_sql:='select ';
if (str_result_col is null or str_result_col='') then raise_application_error(-20002,'需要输出的字段不能为空');
end if;
v_str_query_sql:=v_str_query_sql||str_result_col||' from '||str_query_table||' where 1=1 ';
------当查询条件不为空时,把相应的查询条件拼接到sql语句中
if (str_query_condition is not null and str_query_condition <> '')
then
v_str_query_sql:=v_str_query_sql||str_query_condition;
end if;
----当查询的条件不等于空,将查询条件拼接到sql中
if (str_order_condition is not null and str_order_condition <> '')
then
v_str_query_sql:=v_str_query_sql||' order by '||str_order_condition;
if str_order_style is not null and str_order_style <> ''
then v_str_query_sql:=v_str_query_sql||' '||str_order_style;
end if;
end if;
v_str_query_sql:='select * from ( select A.*,rownum rn from ( '||v_str_query_sql ||' ) A where rownum <='||v_i_end_record||' ) B where rn >= '||v_i_start_record;
dbms_output.put_line(v_str_query_sql);
open cur_query_result for v_str_query_sql;
end p_query_page;
ok 现在看下怎么样调用上面的存储过程
package com.softeem.dbc;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import oracle.jdbc.driver.OracleTypes;
public class DBConnection {
private final static String DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver";
private final static String URL = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
private final static String USER_NAME = "tangzheng";
private final static String PASSWORD = "123456";
public static Connection getconnection() {
Connection conn = null;
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
System.out.println("驱动加载失败");
e.printStackTrace();
}
try {
conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
} catch (SQLException e) {
System.out.println("获取连接失败");
e.printStackTrace();
}
return conn;
}
public PageDTO findOnePage(String pageAction, int pagesize, int currentPage)
throws SQLException {
String table = "tb_employee emp";
String queryCondition = "emp.emp_id<15";
String resultcol = "*";
String orderCondition = "emp.emp_id";
String orderstyle = "asc";
Connection conn = getconnection();
PageDTO pageinfo = null;
try {
CallableStatement cs = conn
.prepareCall("{call p_query_page(?,?,?,?,?,?,?,?,?,?,?)}");
cs.setString(1, pageAction);
cs.setString(2, table);
cs.setString(3, queryCondition);
cs.setString(4, resultcol);
cs.setString(5, orderCondition);
cs.setString(6, orderstyle);
cs.setInt(7, pagesize);
cs.setInt(8, currentPage);
cs.registerOutParameter(7, OracleTypes.INTEGER);
cs.registerOutParameter(8, OracleTypes.INTEGER);
cs.registerOutParameter(9, OracleTypes.INTEGER);
cs.registerOutParameter(10, OracleTypes.INTEGER);
cs.registerOutParameter(11, OracleTypes.CURSOR);
cs.execute();
pageinfo = new PageDTO();
pageinfo.setCurrentPage(cs.getInt(7));
pageinfo.setCurrentPage(cs.getInt(8));
pageinfo.setTotalRecord(cs.getInt(9));
pageinfo.setTotalPage(cs.getInt(10));
ResultSet rs = (ResultSet) cs.getObject(11);
List employees = new ArrayList();
EmployeeDTO employee = null;
while (rs.next()) {
employee = new EmployeeDTO();
employee.setEmpId(rs.getInt("emp_id"));
employee.setEmpName(rs.getString("emp_name"));
employee.setSex(rs.getString("sex"));
employee.setSal(rs.getDouble("sal"));
employees.add(employee);
}
pageinfo.setResult(employees);
} finally {
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
return pageinfo;
}
public static void main(String[] args) {
String pageAction = "nextPage";
int pagesize = 5;
int currentpage = 2;
DBConnection db = new DBConnection();
try {
PageDTO pageinfo = db
.findOnePage(pageAction, pagesize, currentpage);
List<EmployeeDTO> list = pageinfo.getResult();
System.out.println("总页数:" + pageinfo.getCurrentPage());
System.out.println("总记录数:" + pageinfo.getTotalRecord());
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).toString());
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}