在Java中调用存储的过程
Java调用存储过程,一直提示必须声明标识符

Java调⽤存储过程,⼀直提⽰必须声明标识符近⽇使⽤Java调⽤Oracle存储过程,实际上是packages代码如下:public void executeProcedures(String consid){Connection con = DaoUtil.getConnection();try {con.setAutoCommit(false);CallableStatement call = con.prepareCall("{call usetocanevaluate.canevaluate(?)}");call.setString(1, consid);call.execute();con.close();} catch (SQLException e) {e.printStackTrace();}}usetocanevaluate.canevaluate(?) 是Oracle建⽴的存储过程,在PL/SQL中调⽤call usetocanevaluate.canevaluate('1332512150578'); 完全可以执⾏,但是在Java中⼀直提⽰PLS-00201: 必须声明标识符 'USETOCANEVALUATE.CANEVALUATE'经过分析,发现问题原因:必须指定哪个⽤户下的存储过程,即获取连接的时候要⽤存储过程所属的⽤户登录。
解决:Connection con = DaoUtil.getConnection(“admin”);此处的getConnection是项⽬中封装好的⽅法,默认获取default的连接,加上参数则以参数为⽤户获取连接,如果出现同样错误,修改获取连接时候的⽤户名为存储过程所属的⽤户即可。
存储过程调用java代码的方法

存储过程调用java代码的方法存储过程是一组SQL语句的集合,可以被视为一种预编译的SQL 语句,可以通过存储过程调用来实现对数据库的操作。
在Java中,我们可以通过以下方法来调用存储过程:1. 使用JDBC调用存储过程使用JDBC调用存储过程需要以下步骤:1)获取数据库连接。
2)创建CallableStatement对象,使用存储过程名称作为参数创建对象。
3)设置输入参数和输出参数。
输入参数可以通过setXXX()方法设置,输出参数可以使用registerOutParameter()方法注册。
4)执行存储过程。
可以使用execute()或executeUpdate()方法执行。
5)获取输出参数。
以下是一个简单的示例:```try(Connection conn = DriverManager.getConnection(url, user, password)) {CallableStatement cs = conn.prepareCall('{call procedure_name(?, ?)}');cs.setInt(1, 1); // 设置输入参数cs.registerOutParameter(2, Types.VARCHAR); // 注册输出参数cs.execute(); // 执行存储过程String result = cs.getString(2); // 获取输出参数System.out.println(result);} catch (SQLException e) {e.printStackTrace();}```2. 使用ORM框架调用存储过程使用ORM框架调用存储过程需要根据不同的框架进行配置。
以下是使用MyBatis调用存储过程的示例:1)在Mapper XML文件中定义存储过程调用语句。
```<select id='callProcedure' statementType='CALLABLE'>{call procedure_name(#{param1, mode=IN,jdbcType=INTEGER}, #{param2, mode=OUT, jdbcType=VARCHAR})}</select>```2)在Java代码中调用Mapper方法。
jdbc call 用法

jdbc call 用法JDBC(Java Database Connectivity)是一种用于在Java应用程序中与数据库进行交互的API。
JDBC Call是JDBC API的一部分,用于调用存储过程或函数。
下面我将从多个角度来介绍JDBC Call的用法。
1. 调用存储过程:要使用JDBC调用存储过程,首先需要创建一个CallableStatement对象。
这可以通过Connection对象的prepareCall方法来实现。
例如:java.String storedProcedureCall = "{callprocedureName(?, ?)}";CallableStatement cstmt =conn.prepareCall(storedProcedureCall);在这个例子中,procedureName是存储过程的名称,而问号代表参数的占位符。
接下来,可以使用setXXX方法为每个参数设置值,然后执行存储过程并获取结果:java.cstmt.setInt(1, parameter1);cstmt.setString(2, parameter2);cstmt.execute();ResultSet rs = cstmt.getResultSet();// 处理结果集。
2. 调用函数:如果要调用数据库中的函数,也可以使用CallableStatement对象。
与调用存储过程类似,需要准备一个CallableStatement对象,并使用setXXX方法设置参数,然后执行函数并获取结果。
例如:java.String functionCall = "{? = call functionName(?, ?)}";CallableStatement cstmt =conn.prepareCall(functionCall);cstmt.registerOutParameter(1, Types.INTEGER);cstmt.setInt(2, parameter1);cstmt.setString(3, parameter2);cstmt.execute();int result = cstmt.getInt(1);在这个例子中,functionName是函数的名称,registerOutParameter方法用于注册返回值的类型和位置。
通过XFire实现AS与Java通信访问SQL Server存储过程

jC二 二
技术的结合,现在己成为热点, 作为表示层, a h 无提是比较好的效果,并且得到多 l f s 方的支持,AS 存储过程,以求在批1 处理数据时,提升性能的雷要 。
存储过程
中图分类号: TP3
文献标识码: A
文章编号: 1672一 1( 2007) 12(c)一 37, 0096一 01
(3)通过Myeclipse 调用存储过程, 并构 be l Web s ervice 是一种新的web 应用程 建We s rvice(因为Myecl pse all in one 集 成T Xfir e 框架) 序分支 ,他们是 自包含、 自描述 、模块化 1)通过 File一 New 一 oiect , Pr 找到 Web 的应用,可以发布、定位、通过w eb 调用。 Web s r i e 可以执行从简单的请求到复杂 Ser vice Pr oject ,建一个名为lxlmageProc e v c 的Webserv c 项目, i e 该步骤过程, c l 佛e Mye 商务处理的任何功能。一旦部署以后 ,其 动导人 Xfi r e 相关的核心 i a r 文件 他Web s erv c 应用程序可以发现并调用 会 自 i e 2)新建Webservice , 并勾选create new 它部署的服务 。 XFi re 是一个免费的开源SOAP 框架, b e a n ,这样会自动生成一个名与 它不仅可以极大方便地 实现这 样一个环 webser vice 名类同的名称, 其主体内容如
var temp=new Array(); temp= result .split(”” : ); _global .images=temP[2」 .sPlit (”” ,);
1)在数据库名为lxlmageAdver 中建立 表名为adverimage_active , 其中有三个字
jdbctemplate调用存储过程返回 map对象

要使用JdbcTemplate调用存储过程并返回Map对象,可以按照以下步骤操作:1. 创建一个存储过程。
例如,创建一个名为`get_employee_info`的存储过程,该过程接收一个参数`emp_id`,并返回一个包含员工信息的Map对象。
```sqlCREATE PROCEDURE get_employee_info(IN emp_id INT, OUT result MAP<VARCHAR, VARCHAR>)BEGINSELECT * INTO result FROM employee WHERE id = emp_id;END;```2. 在Java代码中,使用JdbcTemplate调用存储过程。
首先,注入JdbcTemplate实例,然后使用`callProcedure`方法调用存储过程。
将结果存储在一个`Map`对象中。
```javaimport org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.util.HashMap;import java.util.List;import java.util.Map;public class EmployeeService {private JdbcTemplate jdbcTemplate;public EmployeeService(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}public Map<String, Object> getEmployeeInfo(int empId) {Map<String, Object> result = new HashMap<>();jdbcTemplate.callProcedure("{call get_employee_info(?, ?)}", empId, result);return result;}}```3. 在上述示例中,我们使用了`callProcedure`方法的第一个参数来传递存储过程的名称,第二个参数用于传递输入参数(在本例中为`emp_id`),第三个参数用于传递输出参数(在本例中为`result`)。
oracle存储过程调用javasource的方法

oracle存储过程调用javasource的方法Oracle存储过程调用Java方法介绍在Oracle数据库中,我们可以使用存储过程来执行一系列的数据库操作。
有时候,我们希望在存储过程中调用Java方法,以实现一些比较复杂的逻辑。
本文将详细介绍在Oracle存储过程中调用Java方法的几种方法。
方法一:使用Java Stored ProcedureJava Stored Procedure是Oracle数据库提供的一种特殊的存储过程类型,在该类型的存储过程中可以直接调用Java方法。
步骤:1.创建Java类,并将其编译成字节码文件(.class文件)。
2.将字节码文件导入到数据库中,可以使用loadjava工具或通过SQL语句执行导入。
3.创建Java Stored Procedure,将其指定为刚导入的字节码文件,并编写实际的存储过程逻辑。
4.在需要的地方调用Java Stored Procedure。
优点:•简单易用,只需创建Java类和Java Stored Procedure即可。
•可以直接访问数据库,无需通过其他方式。
缺点:•必须将Java类编译成字节码文件并导入到数据库中,稍显麻烦。
•Java Stored Procedure在数据库中运行,可能会造成数据库性能的损耗。
方法二:使用外部过程调用Java方法Oracle数据库通过提供外部过程功能,允许我们在存储过程中调用外部的Java方法。
步骤:1.将Java方法包装成Java函数或Java过程,并将其编译成动态加载库文件(.so或.dll文件)。
2.使用CREATE LIBRARY语句在数据库中创建对应的外部库。
3.创建存储过程,将其指定为调用外部库中的函数或过程,并编写实际的存储过程逻辑。
4.在需要的地方调用存储过程。
优点:•可以方便地调用已存在的Java方法,无需修改原有代码。
•外部过程在数据库外部运行,不会对数据库性能造成影响。
如何在Java程序中调用存储过程

如何在Java程序中调用存储过程(一)?(1)使用scott/tiger用户在Oracle中创建2个表并插入几条数据。
Create table carl_test(A varchar2(200));create table carl_test1(B varchar2(200));--insert some data into carl_test1 tableinsert into carl_test1 values('carl1');insert into carl_test1 values('carl2');insert into carl_test1 values('carl3');commit;(2)使用scott/tiger用户在Oracle中创建一个测试存储过程,该存储过程有三个参数,第一个参数为输入参数,最后两个参数为输出参数。
为简单起见,没有在该存储过程中加入异常捕获机制。
CREATE OR REPLACE PROCEDURE carl_sp_test( v_monthly IN varchar2,last_inserted_rows_num OUT number,all_effected_rows_num OUT number)ISBEGIN/*删除carl_test表中所有记录*/delete carl_test;/*将删除行数赋值给总影响行数*/all_effected_rows_num := SQL%Rowcount;commit;/*将用户输入参数插入carl_test表中*/insert into carl_test(a) values(v_monthly);all_effected_rows_num:= all_effected_rows_num + SQL%Rowcount;/*将表carl_test1中的所有记录插入到carl_test1中*/insert into carl_testselect* from carl_test1;/*获得影响记录数*/last_inserted_rows_num:=SQL%Rowcount;all_effected_rows_num:= all_effected_rows_num + SQL%Rowcount;commit;END carl_sp_test;(3)使用scott/tiger用户在SQL/Plus中测试上述存储过程SQL> variable all_effected_rows_num number;SQL> variable last_inserted_rows_num number;SQL> exec carl_sp_test('first var',:last_inserted_rows_num,:all_effected_rows_num);PL/SQL procedure successfully completedlast_inserted_rows_num---------3all_effected_rows_num---------4SQL> print last_inserted_rows_num;last_inserted_rows_num---------3SQL> print all_effected_rows_num;all_effected_rows_num---------4SQL>上述结果表示测试成功(4)使用下面的Java类TestStoreProcedure.java测试存储过程。
JAVA通过MyBatis调用MySql存储过程和函数

JAV A通过MyBatis调用MySql存储过程和函数1.引言无论是采用SPRING MVC框架开发WEB画面,还是开发需按一定时间间隔执行的批处理,都可能要调用数据库的存储过程或函数。
其间调用参数设置不正会浪费大量调试时间初学者甚至放弃使用存储过程。
本文记录了通过MyBatis调用MySql存储过程和函数的具体参数设置内容,供参考。
2.MySql存储过程例/*全公司员工下一年度带薪休假一发赋予处理*/CREATE DEFINER=`DBuser`@`%` PROCEDURE `paid_vacation_compute `( OUT p_返回值 INT(11) ,INOUT p_员工号 CHAR(3) ,p_操作者ID VARCHAR(3))PROC_START:BEGIN/* 变量声明 */DECLARE done INT; #异常退出控制变量DECLARE empNo CHAR(3); #员工号DECLARE dateHire date; #分公司就职日DECLARE workYears INT; #集团内工作年数DECLARE lastYearRemainDays FLOAT; #昨年残日数(允许以小时为单位休假)DECLARE nowYearleaveDays FLOAT; #今年休暇日数(允许以小时为单位休假)DECLARE elapseYear INT; #入集团经过年度数/* 游标声明 */#上年带薪休假数据DECLARE staffPaidVacationDaysCur CURSOR FORSELECT a.EMP_NO, #员工号a.DATE_HIRE, #入职日期a.WORK_YEARS, #工作年限b.REMAIN_DAYS # 上年带薪休假应休但未休残日数FROM T_EMPLOYEE AS a,T_PAID_VACATION AS bWHERE a. EMP_NO = b. EMP_NO/* 程序退出规定声明 */DECLARE CONTINUE HANDLER FOR NOT FOUND SET SET done = 1;DECLARE EXIT HANDLER FOR SQLWARNING SET done = 2;DECLARE CONTINUE HANDLER FOR SQLEXCEPTIONBEGINSET done = 3;ROLLBACK; #SQL异常回滚END;/* 1.输入参数妥当性检查 */IF (p_操作者 IS NULL OR p_操作者 = '') THENSET p_返回值 = 9;LEAVE PROC_START;END IF;SET done = 0;START TRANSACTION;loop_label:LOOP# 读入一条记录, 代入到各个变量中FETCH staffPaidVacationDaysCurINTO empNo, #员工号dateHire, #就职日workYears, #集团内工作年数lastYearRemainDays #昨年残日数;IF done = 1 THENLEAVE loop_label;END IF;/*根据集团内工作年限+分公司就职年限计算下一年度带薪天数*/ SET elapseYear = YEAR(current_timestamp)- Year(dateHire)+ workYears;IF elapseYear = 0 THENSET nowYearLeaveDays = 10;ELSEIF elapseYear = 1 THENSET nowYearLeaveDays = 11;ELSEIF elapseYear = 2 THENSET nowYearLeaveDays = 12;ELSEIF elapseYear = 3 THENSET nowYearLeaveDays = 14;ELSEIF elapseYear = 4 THENSET nowYearLeaveDays = 16;ELSEIF elapseYear = 5 THENSET nowYearLeaveDays = 18;ELSEIF elapseYear >= 6 THENSET nowYearLeaveDays = 20;END IF;SET done = 0;SET p_员工号= empNo;UPDATE T_PAID_VACATIONSETLAST_YEAR_REMAIN_DAYS = lastYearRemainDays,THIS_YEAR_BASE_DAYS = nowYearLeaveDays,UPDATE_DATETIME = current_timestamp,UPDATE_USER_ID = 'SYS',UPDATE_TERMINAL_ID = 'MANUAL'WHERE EMP_NO = CONVERT(empNo USING binary);IF done = 3 THENSET p_返回值 = 6;LEAVE PROC_START;END IF;END LOOP;COMMIT;END3.MySql函数例CREATE DEFINER=`DBuser`@`%` FUNCTION ` paid_vacation_compute `( p_员工号 CHAR(3) ,p_操作者ID VARCHAR(3))) RETURNS int(11)BEGIN/* 变量声明 */DECLARE done INT; #异常退出控制变量DECLARE empNo CHAR(3); #员工号DECLARE dateHire date; #分公司就职日DECLARE workYears INT; #集团内工作年数DECLARE lastYearRemainDays FLOAT; #昨年残日数(允许以小时为单位休假)DECLARE nowYearleaveDays FLOAT; #今年休暇日数(允许以小时为单位休假)DECLARE elapseYear INT; #入集团经过年度数/* 游标声明 */#上年带薪休假数据DECLARE staffPaidVacationDaysCur CURSOR FORSELECT a.EMP_NO, #员工号a.DATE_HIRE, #入职日期a.WORK_YEARS, #工作年限b.REMAIN_DAYS # 上年带薪休假应休但未休残日数FROM T_EMPLOYEE AS a,T_PAID_VACATION AS bWHERE a. EMP_NO = b. EMP_NO/* 程序退出规定声明 */DECLARE CONTINUE HANDLER FOR NOT FOUND SET SET done = 1;DECLARE EXIT HANDLER FOR SQLWARNING SET done = 2;DECLARE CONTINUE HANDLER FOR SQLEXCEPTIONBEGINSET done = 3;ROLLBACK; #SQL异常回滚END;/* 1.输入参数妥当性检查 */IF (p_操作者 IS NULL OR p_操作者 = '') THENRETURN 9;END IF;SET done = 0;START TRANSACTION;loop_label:LOOP# 读入一条记录, 代入到各个变量中FETCH staffPaidVacationDaysCurINTO empNo, #员工号dateHire, #就职日workYears, #集团内工作年数lastYearRemainDays #昨年残日数;IF done = 1 THENLEAVE loop_label;END IF;/*根据集团内工作年限+分公司就职年限计算下一年度带薪天数*/ SET elapseYear = YEAR(current_timestamp)- Year(dateHire)+ workYears;IF elapseYear = 0 THENSET nowYearLeaveDays = 10;ELSEIF elapseYear = 1 THENSET nowYearLeaveDays = 11;ELSEIF elapseYear = 2 THENSET nowYearLeaveDays = 12;ELSEIF elapseYear = 3 THENSET nowYearLeaveDays = 14;ELSEIF elapseYear = 4 THENSET nowYearLeaveDays = 16;ELSEIF elapseYear = 5 THENSET nowYearLeaveDays = 18;ELSEIF elapseYear >= 6 THENSET nowYearLeaveDays = 20;END IF;SET done = 0;SET p_员工号= empNo;UPDATE T_PAID_VACATIONSETLAST_YEAR_REMAIN_DAYS = lastYearRemainDays,THIS_YEAR_BASE_DAYS = nowYearLeaveDays,UPDATE_DATETIME = current_timestamp,UPDATE_USER_ID = 'SYS',UPDATE_TERMINAL_ID = 'MANUAL'WHERE EMP_NO = CONVERT(empNo USING binary);IF done = 3 THENRETURN 6;END IF;END LOOP;COMMIT;END4.MySql存储过程调用时的iBatis用Mapper例BaseInfoEditMapper.xml<?xml version="1.0"encoding="UTF-8"?><!DOCTYPE mapperPUBLIC"-////DTD Mapper 3.0//EN""/dtd/ibatis-3-mapper.dtd"><mapper namespace="com.xxx.web.mapper.base_info_edit"><select id="VacationProcedure"parameterType="VacationBean"statementType="CALLABLE"> { call paid_vacation_compute (#{ReturnValue,javaType=INTEGER, jdbcType=INTEGER, mode=OUT},#{StaffNumber,javaType=String, jdbcType=CHAR, mode=INOUT},#{HireDate,javaType=String, jdbcType=VARCHAR, mode=IN},#{OperateID,javaType=String, jdbcType=VARCHAR, mode=IN})}</select></mapper>5.MySql函数调用时的iBatis用Mapper例BaseInfoEditMapper.xml<?xml version="1.0"encoding="UTF-8"?><!DOCTYPE mapperPUBLIC"-////DTD Mapper 3.0//EN""/dtd/ibatis-3-mapper.dtd"><mapper namespace="com.xxx.web.mapper.base_info_edit"><select id="VacationProcedure"parameterType="VacationBean"statementType="CALLABLE"> { #{ReturnValue,javaType=INTEGER, jdbcType=INTEGER, mode=OUT} =call paid_vacation_compute (#{StaffNumber,javaType=String, jdbcType=CHAR, mode=IN},#{HireDate,javaType=String, jdbcType=VARCHAR, mode=IN},#{OperateID,javaType=String, jdbcType=VARCHAR, mode=IN} )} </select></mapper>6.JAVA调用例1(MySql存储过程和函数相同)package com.XXX.impl;import java.util.ArrayList;import java.util.List;import com.XXX.web.bean.VacationCreateBean;import com.XXX.web.dao.BaseInfoEditDAO;import com.XXX.web.util.BasicSqlSupport;public class BaseInfoEditDAOImpl extends BasicSqlSupport implements BaseInfoEditDAO { public boolean addBaseInfo(BaseInfoEditBean objUserInfo) throws Exception {boolean blnFlag=false;//成功FLAG;//全公司员工下一年度带薪休假一发赋予处理VacationCreateBean objVacationCreateBean = new VacationCreateBean();objVacationCreateBean.setStaffNumber(objUserInfo.getSTAFF_NUMBER());objVacationCreateBean.setHireDate(objUserInfo.getDATE_HIRE().toString());objVacationCreateBean.setOperateID(objUserInfo.getCREATE_USER_ID());objVacationCreateBean.setDhcWorkYearsShinKi(objUserInfo.getDHC_WORK_YEARS());String returnValue = (String)this.session.selectOne("com.XXX.web.mapper.base_info_edit.VacationProcedure", objVacationCreateBean);//System.out.println("staffNumber=" + objVacationCreateBean.getStaffNumber());//System.out.println("result=" + objVacationCreateBean.getReturnValue());//System.out.println("returnValue=" + returnValue);//追加結果の判断blnFlag=true;return blnFlag;}}7.処理DAO接口package com.XXX.web.dao;import java.util.List;import com.XXX.web.bean.BaseInfoEditBean;/*** 员工基本信息画面の処理DAO*/public interface BaseInfoEditDAO {public List<BaseInfoEditBean> selectAuthoriyList() throws Exception;public String selectStaffId() throws Exception;public int selectOpetateTimeNum(String strStaffNumber) throws Exception;public boolean addBaseInfo(BaseInfoEditBean objUserInfo) throws Exception;public boolean updateBaseInfo(BaseInfoEditBean objUserInfo) throws Exception;public BaseInfoEditBean searchBaseInfo(String strStaffNumber) throws Exception; }8.共同処理package com.XXX.web.util;import org.apache.ibatis.session.SqlSession;public class BasicSqlSupport{protected SqlSession session;public SqlSession getSession() {return session;}public void setSession(SqlSession session) {this.session = session;} }9. DAO与MAP间的交互BEANpackage com.XXX.web.bean;import java.io.Serializable;public class VacationCreateBean implements Serializable{private int ReturnValue;private String StaffNumber;private String HireDate;private String OperateID;private int WorkYearsShinKi;public int getReturnValue() {return ReturnValue;}public void setReturnValue(int returnValue) {ReturnValue = returnValue;}public String getStaffNumber() {return StaffNumber;}public void setStaffNumber(String staffNumber) {StaffNumber = staffNumber;}public String getHireDate() {return HireDate;}public void setHireDate(String hireDate) {HireDate = hireDate;}public String getOperateID() {return OperateID;}public void setOperateID(String operateID) {OperateID = operateID;}public int getDhcWorkYearsShinKi() {return dhcWorkYearsShinKi;}public void setDhcWorkYearsShinKi(int dhcWorkYearsShinKi) {this.dhcWorkYearsShinKi = dhcWorkYearsShinKi;}}10.通过MAIN函数进行调用package com.ohc.pms.batch;import java.io.FileInputStream;import java.io.IOException;import java.sql.CallableStatement;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;import org.apache.log4j.Logger;import org.apache.log4j.PropertyConfigurator;public class VacationCreate {/**JDBC驱动名 */static String jdbcDriver = "com.mysql.jdbc.Driver";/**DB URL */static String dbURL = "jdbc:mysql://172.999.999.35:3306/empdb";/** DB用户名 */static String user = "empuser";/**DB密码 */static String pass = "empuser123";/**日志输出 */static protected Logger log = Logger.getLogger(VacationCreate.class );public static void main(String[] args) {Connection conn = null;CallableStatement stmt = null;try{PropertyConfigurator.configure(System.getProperty("user.dir") + "\\" + "log4j.properties");("実行路径:" + System.getProperty("user.dir"));String fileName = "jdbc.properties"; // 属性文件名Properties conf = new Properties();try {conf.load(new FileInputStream(System.getProperty("user.dir") + "\\" + "jdbc.properties"));} catch (IOException e) {System.err.println("Cannot open " + fileName + ".");e.printStackTrace();System.exit(-1); // 程序終了}// 读入jdbcDriver = conf.getProperty("driver");dbURL = conf.getProperty("url");user = conf.getProperty("user");pass = conf.getProperty("password");//JDBC driver登録Class.forName("com.mysql.jdbc.Driver");("DB连接。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在Java中调用存储的过程在Java中调用存储的过程导语:存储过程是指保存在数据库并在数据库端执行的程序。
你可以使用特殊的语法在Java类中调用存储过程。
在调用时,存储过程的名称及指定的参数通过JDBC连接发送给DBMS,执行存储过程并通过连接(如果有)返回结果。
下面是在Java中调用存储的过程,一起来学习下吧:使用存储过程简单的老的JDBC通过CallableStatement类支持存储过程的调用。
该类实际上是PreparedStatement的一个子类。
假设我们有一个poets数据库。
数据库中有一个设置诗人逝世年龄的存储过程。
下面是对老酒鬼Dylan Thomas(old soak Dylan Thomas,不指定是否有关典故、文化,请批评指正。
译注)进行调用的详细代码:try{int age = 39;String poetName = "dylan thomas";CallableStatement proc = connection.prepareCall("{ call set_death_age(?, ?) }");proc.setString(1, poetName);proc.setInt(2, age);cs.execute();}catch (SQLException e){ // ....}传给prepareCall方法的字串是存储过程调用的书写规范。
它指定了存储过程的名称,?代表了你需要指定的参数。
和JDBC集成是存储过程的一个很大的便利:为了从应用中调用存储过程,不需要存根(stub)类或者配置文件,除了你的DBMS的JDBC 驱动程序外什么也不需要。
当这段代码执行时,数据库的存储过程就被调用。
我们没有去获取结果,因为该存储过程并不返回结果。
执行成功或失败将通过例外得知。
失败可能意味着调用存储过程时的失败(比如提供的一个参数的类型不正确),或者一个应用程序的失败(比如抛出一个例外指示在poets数据库中并不存在“Dylan Thomas”)结合SQL操作与存储过程映射Java对象到SQL表中的行相当简单,但是通常需要执行几个SQL语句;可能是一个SELECT查找ID,然后一个INSERT插入指定ID 的数据。
在高度规格化(符合更高的范式,译注)的数据库模式中,可能需要多个表的更新,因此需要更多的语句。
Java代码会很快地膨胀,每一个语句的网络开销也迅速增加。
将这些SQL语句转移到一个存储过程中将大大简化代码,仅涉及一次网络调用。
所有关联的SQL操作都可以在数据库内部发生。
并且,存储过程语言,例如PL/SQL,允许使用SQL语法,这比Java代码更加自然。
下面是我们早期的存储过程,使用Oracle的PL/SQL语言编写:create procedure set_death_age(poet VARCHAR2, poet_age NUMBER)poet_id NUMBER;begin SELECT id INTO poet_id FROM poets WHERE name = poet;INSERT INTO deaths (mort_id, age) VALUES (poet_id, poet_age);end set_death_age;很独特?不。
我打赌你一定期待看到一个poets表上的UPDATE。
这也暗示了使用存储过程实现是多么容易的一件事情。
set_death_age几乎可以肯定是一个很烂的实现。
我们应该在poets 表中添加一列来存储逝世年龄。
Java代码中并不关心数据库模式是怎么实现的,因为它仅调用存储过程。
我们以后可以改变数据库模式以提高性能,但是我们不必修改我们代码。
下面是调用上面存储过程的Java代码:public static void setDeathAge(Poet dyingBard, int age)throws SQLException{Connection con = null;CallableStatement proc = null;try {con = connectionPool.getConnection();proc = con.prepareCall("{ call set_death_age(?, ?) }");proc.setString(1, dyingBard.getName());proc.setInt(2, age);proc.execute();}finally {try { proc.close(); }catch (SQLException e) {}con.close();}}为了确保可维护性,建议使用像这儿这样的static方法。
这也使得调用存储过程的代码集中在一个简单的模版代码中。
如果你用到许多存储过程,就会发现仅需要拷贝、粘贴就可以创建新的方法。
因为代码的模版化,甚至也可以通过脚本自动生产调用存储过程的代码。
Functions存储过程可以有返回值,所以CallableStatement类有类似getResultSet这样的方法来获取返回值。
当存储过程返回一个值时,你必须使用registerOutParameter方法告诉JDBC驱动器该值的SQL 类型是什么。
你也必须调整存储过程调用来指示该过程返回一个值。
下面接着上面的例子。
这次我们查询Dylan Thomas逝世时的年龄。
这次的存储过程使用PostgreSQL的pl/pgsql:create function snuffed_it_when (VARCHAR) returns integer 'declarepoet_id NUMBER;poet_age NUMBER;begin--first get the id associated with the poet.SELECT id INTO poet_id FROM poets WHERE name = $1;--get and return the age.SELECT age INTO poet_age FROM deaths WHERE mort_id = poet_id;return age;end;' language 'pl/pgsql';另外,注意pl/pgsql参数名通过Unix和DOS脚本的$n语法引用。
同时,也注意嵌入的注释,这是和Java代码相比的另一个优越性。
在Java中写这样的注释当然是可以的,但是看起来很凌乱,并且和SQL语句脱节,必须嵌入到Java String中。
下面是调用这个存储过程的Java代码:connection.setAutoCommit(false);CallableStatement proc = connection.prepareCall("{ ? = call snuffed_it_when(?) }");proc.registerOutParameter(1, Types.INTEGER);proc.setString(2, poetName);cs.execute();int age = proc.getInt(2);如果指定了错误的返回值类型会怎样?那么,当调用存储过程时将抛出一个RuntimeException,正如你在ResultSet操作中使用了一个错误的类型所碰到的一样。
复杂的返回值关于存储过程的知识,很多人好像就熟悉我们所讨论的这些。
如果这是存储过程的全部功能,那么存储过程就不是其它远程执行机制的替换方案了。
存储过程的功能比这强大得多。
当你执行一个SQL查询时,DBMS创建一个叫做cursor(游标)的数据库对象,用于在返回结果中迭代每一行。
ResultSet是当前时间点的游标的一个表示。
这就是为什么没有缓存或者特定数据库的支持,你只能在ResultSet中向前移动。
某些DBMS允许从存储过程中返回游标的一个引用。
JDBC并不支持这个功能,但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针(pointer)。
设想列出所有没有活到退休年龄的诗人,下面是完成这个功能的存储过程,返回一个打开的游标,同样也使用PostgreSQL的pl/pgsql语言:create procedure list_early_deaths () return refcursor as 'declaretoesup refcursor;beginopen toesup for SELECT , deaths.age FROM poets, deaths -- all entries in deaths are for poets. -- but the table might become generic.WHERE poets.id = deaths.mort_id AND deaths.age < 60;return toesup;end;' language 'plpgsql';下面是调用该存储过程的Java方法,将结果输出到PrintWriter:PrintWriter:static void sendEarlyDeaths(PrintWriter out){Connection con = null;CallableStatement toesUp = null;try {con = ConnectionPool.getConnection();// PostgreSQL needs a transaction to do this... con.setAutoCommit(false); // Setup the call.CallableStatement toesUp = connection.prepareCall("{ ? = call list_early_deaths () }");toesUp.registerOutParameter(1, Types.OTHER);toesUp.execute();ResultSet rs = (ResultSet) toesUp.getObject(1);while (rs.next()) {String name = rs.getString(1);int age = rs.getInt(2);out.println(name + " was " + age + " years old.");}rs.close();}catch (SQLException e) { // We should protect these calls. toesUp.close(); con.close();}}因为JDBC并不直接支持从存储过程中返回游标,我们使用Types.OTHER来指示存储过程的返回类型,然后调用getObject()方法并对返回值进行强制类型转换。