如何从 jdbc 调用存储的函数?
Posted
技术标签:
【中文标题】如何从 jdbc 调用存储的函数?【英文标题】:how to call a stored function from jdbc? 【发布时间】:2013-10-12 10:10:14 【问题描述】:我正在使用自定义标签创建一个登录页面,在下面的代码中,我想执行一个存储的 oracle 函数,该函数将采用 2 个参数(名称、密码)进行身份验证并返回一个数字。但是当我编译下面的代码时,它给出了一个错误说(发现:int)不兼容的类型。请告诉我我哪里出错了?我是否正确调用了该函数?
package pack.java;
import pack.java.MyModel;
import java.io.*;
import java.lang.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.sql.*;
public class MyController extends TagSupport
HttpServletRequest request;
HttpServletResponse response;
public int doStartTag()throws JspException
request = (HttpServletRequest)pageContext.getRequest();
response = (HttpServletResponse)pageContext.getResponse();
return EVAL_PAGE;
public void check()
HttpSession mysession = request.getSession();
Connection con;
CallableStatement stmt;
JspWriter out = pageContext.getOut();
try
Class.forName("oracle.jdbc.driver.OracleDriver");
catch(ClassNotFoundException ex)
try
String aa = (String)MyModel.name.trim();
String bb = (String)MyModel.pass.trim();
con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE","gaurav","oracle");
stmt = con.prepareCall("select usercheck1(?,?) from dual");
stmt.setString(1, aa);
stmt.setString(2, bb);
rs = stmt.executeQuery();
try
while (rs.next())
String empid = rs.getString (1);
mysession.setAttribute("user", empid);
if (empid != null)
response.sendRedirect("/Myjsp/selectaction.jsp");
else
out.println("InValid User");
catch(Exception ex)
catch(SQLException ex)
public int doEndTag() throws JspException
check();
return EVAL_PAGE;
下面是存储函数
create or replace function usercheck1
(uname varchar2, upass varchar2)
return number
as
numb number;
begin
select (employe_id)
into numb
from record
where name = uname
and password = upass;
return numb;
end usercheck1;
/
使用下面的语句执行函数
select usercheck1 ('ghg','aa') from dual;
【问题讨论】:
在哪一行报错? 它说找到不兼容的类型:int,需要:java.sql.ResultSet,在线 rs=stmt.executeUpdate(); 在不久的将来,请转到Code Review 来审核您的格式。 【参考方案1】:要执行一个函数,规范的方法是使用CallableStatement
。这是PreparedStatement
的一个特例(子类)。
此外,您必须使用registerOutParameter
方法之一指定right 输出参数类型。一旦调用完成 (execute
),您就可以从语句本身中提取输出值。不是通过ResultSet
。
所有这一切都会导致类似:
CallableStatement stmt = con.prepareCall("? = call usercheck1(?, ?)");
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Prepare a `call`
stmt.registerOutParameter(1, Types.INTEGER);
# Setup out parameter type ^^^^^^^^^^^^^
stmt.setString(2,aa);
stmt.setString(3,bb);
stmt.execute();
# ^^^^^^^
# execute the statement
int output = stmt.getInt(1);
# ^^^^^^^^^^^
# Extract the result from the statement itself
一种完全不同的方式是实际使用SELECT
查询来调用函数。这是OP在问题中提出的建议。这样,您可以像往常一样使用ResultSet
来提取值:
PreparedStatement stmt = con.prepareStatement("SELECT usercheck1(?, ?) FROM DUAL");
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Prepare a statement
stmt.setString(2,aa);
stmt.setString(3,bb);
ResultSet rs = stmt.executeQuery();
# ^^^^^^^^^^^^
# execute the query
while(rs.next())
int output = rs.getInt(1);
# ^^^^^^^^^
# Extract the result from the `ResultSet`
# ...
# do whatever you want with the data
【讨论】:
这不可能……CallableStatement 不是只用于过程而不用于函数吗?否则,如何在第 0 个参数位置获取函数返回值? 其实见:docs.oracle.com/en/database/oracle/oracle-database/18/jjdev/… 为什么需要花括号? 当使用FROM DUAL
时,您不能执行带有数据编辑的程序(仅选择)。【参考方案2】:
首先,您说 proc 返回一个数字,但您的代码需要一个返回字符串的结果集? 无论如何,假设您的 proc 返回一个 int。 更改这些行
stmt=con.prepareCall("call usercheck1(?,?)");
stmt.setString(1,aa);
stmt.setString(2,bb);
rs=stmt.executeUpdate();
到
stmt=con.prepareCall ("? = call usercheck1(?, ?)");
stmt.registerOutParameter (1, Types.INTEGER);
stmt.setString(2,aa);
stmt.setString(3,bb);
stmt.execute();
int output =stmt.getInt (1);
如果您实际上期望 proc 的结果为字符串,则更改 到
stmt=con.prepareCall ("? = call usercheck1(?, ?)");
stmt.registerOutParameter (1, Types.VARCHAR2);
stmt.setString(2,aa);
stmt.setString(3,bb);
stmt.execute();
String output =stmt.getString (1);
ResultSets 用于游标,需要您指定输出参数。 希望有帮助。
http://docs.oracle.com/cd/A84870_01/doc/java.816/a81354/samapp2.htm
如果您使用的是较新版本的 oracle >= 9i,您可能需要使用 begin end 语法。
【讨论】:
感谢您的输入,但是当我尝试通过更改您建议代码不起作用来执行代码时,登录失败,因为我认为该函数没有被执行。我正在更新我创建的函数,请检查可能出了什么问题 除非你告诉我发生了什么以及你现在在做什么,否则我无法告诉你出了什么问题。也许你应该重新发布你的新代码...... 另外,尝试将可调用的 sql 代码更改为 "begin ? := usercheck1(?,?); end;"这是新的 oracle 9i 语法。 我重新发布了代码,当我尝试登录时页面没有重定向。我已经使用了这两个语句( begin ? := usercheck1(?,?); end; 和 ? = call usercheck1(?, ?) ),但都不起作用......我使用的是 oracle 10g【参考方案3】:其他人建议通过 select 语句运行查询,但如果函数执行任何 DML,这将引发异常。使用 CallableStatement 是最好的选择。
【讨论】:
【参考方案4】:这是一个使用 JPA (javax.persistence.EntityManager
) 的更现代的解决方案:
@PersistenceContext
private EntityManager entityManager;
. . .
final int myResult;
Session session = entityManager.unwrap(Session.class);
session.doWork(connection ->
try (CallableStatement function =
connection.prepareCall(" ? = call usercheck1(?, ?) "))
function.registerOutParameter(1, Types.INTEGER);
function.setString(2, aa);
function.setString(3, bb);
function.execute();
myResult = function.getInt(1);
);
这些是指示解决方案适用于哪些 lib 版本的主要依赖项:
compile 'com.oracle:ojdbc6:11.2.0.4.0'
compile 'org.hibernate:hibernate-core:5.4.7.Final'
compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final'
作为doWork()
的替代方法并将存储的函数结果分配给getXxx(1)
,您可以使用doReturningWork()
。
在 Kotlin 中
这是 Kotlin 中的解决方案(并使用 doReturningWork
):
@PersistenceContext
lateinit var entityManager: EntityManager
. . .
val session : Session = entityManager.unwrap(Session::class.java)
var myResult = session.doReturningWork connection: Connection ->
connection.prepareCall(" ? = call usercheck1(?, ?) ").use function ->
function.registerOutParameter(1, Types.INTEGER)
function.setString(2, aa)
function.setString(3, bb)
function.execute()
return@doReturningWork function.getInt(1)
【讨论】:
【参考方案5】:executeUpdate()
返回int
,有关详细信息,请参阅here。我相信您期待的是ResultSet
,
更改下一行,
rs=stmt.executeUpdate();
到
rs=stmt.executeQuery();
请参阅executeQuery 了解更多信息。
【讨论】:
谢谢它的工作.. 虽然我高清使用查询 stmt=con.prepareCall("select usercheck1(?,?) from dual"); executeUpdate的调用应该在事务中以上是关于如何从 jdbc 调用存储的函数?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JDBC 调用带有命名参数的 Sybase 存储过程