请教调用Oracle存储过程的问题,谢谢

ATCG 2012-06-17 07:07:22
有Oracle存储过程如下
create or replace procedure proc_test(p_str varchar2) is
begin
null;
end;
/

有java程序如下
import java.sql.*;
import java.util.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

public class test
{
public static void main(String args[]) throws Exception
{
String outstr ="";
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection con = DriverManager.getConnection("jdbc:oracle:oci8:@local","user", "pass");
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("begin proc_test(?); end;");
long s = System.currentTimeMillis();

//我希望每次客户端(WEB页面)传入参数给这个java程序总是从这一行里开始执行,
//也就是prepareCall存储过程一次,调用存储过程多次,调用次数会很多
cstmt.setString(1,"客户端传入的参数值");
cstmt.execute();

long e = System.currentTimeMillis();
System.out.println(e - s);
}
}
...全文
376 37 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
ATCG 2012-06-21
  • 打赏
  • 举报
回复
明白,多谢,java我还要多学习
[Quote=引用 36 楼 的回复:]
引用 35 楼 的回复:
多谢你了。
“就把Connection简单池化”就是你上面写的代码?可靠么?
如果用于应用?因为听你说的好像风险较大?
能再快0.1豪秒当然最好了,0.1豪秒也是很可贵的。当然不能快0.1豪秒也只能算了。如果考虑到大的风险


把Connection池化,不是我上面写的代码,我只是示意;请使用中间件提供的JDBC池。如果没有,就用c3p0也行。

要再快……
[/Quote]
MiceRice 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 35 楼 的回复:]
多谢你了。
“就把Connection简单池化”就是你上面写的代码?可靠么?
如果用于应用?因为听你说的好像风险较大?
能再快0.1豪秒当然最好了,0.1豪秒也是很可贵的。当然不能快0.1豪秒也只能算了。如果考虑到大的风险
[/Quote]

把Connection池化,不是我上面写的代码,我只是示意;请使用中间件提供的JDBC池。如果没有,就用c3p0也行。

要再快1毫秒,就得把Callable池化,类似我20楼写的 CallablePool 类;但这个类并不完善,风险还是较多的高;感觉你的技术水平还不太足以掌控这个风险,建议不要做。
ATCG 2012-06-21
  • 打赏
  • 举报
回复
多谢你了。
“就把Connection简单池化”就是你上面写的代码?可靠么?
如果用于应用?因为听你说的好像风险较大?
能再快0.1豪秒当然最好了,0.1豪秒也是很可贵的。当然不能快0.1豪秒也只能算了。如果考虑到大的风险


[Quote=引用 31 楼 的回复:]
预跑这个是JVM级别的,全JVM也就第一次90毫秒,以后都不需要了,你要避免个啥啊?

我又写了个并发测试的,也就是说,只要JVM中曾经有地方执行过一次,其它所有线程再执行都是0.6毫秒。


Java code

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLExcep……
[/Quote]
MiceRice 2012-06-20
  • 打赏
  • 举报
回复
楼主,你下结论之前,最好自己写代码多测试下吧。

客户端每次传参数进来,每次也就是0.6毫秒:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

import oracle.jdbc.OracleCallableStatement;

public class TestCall {
private static int TIMES = 1000;

private static String URL = "jdbc:oracle:thin:@192.168.234.128:1521:redhat5";
private static String USER = "zenki";
private static String PASS = "zenki";

public static Connection openConnection() throws SQLException {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
return DriverManager.getConnection(URL, USER, PASS);
}

public static void main(String[] args) throws Exception {
Connection con = openConnection();
String sql = "call proc_test(?)";

// 预跑
SimpleCaller.callProc(con, sql, "");

// 性能测试
long timer = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
SimpleCaller.callProc(con, sql, String.valueOf(i));
}
timer = System.currentTimeMillis() - timer;
System.out.println("\nTime spend: " + timer + "\t Per: " + (1.0 * timer / TIMES));

con.close();
}
}

class SimpleCaller {
public static String callProc(Connection con, String sql, String param) throws SQLException {
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall(sql);
try {
cstmt.registerOutParameter(1, Types.VARCHAR);
cstmt.setString(1, String.valueOf(param));
cstmt.execute();
return cstmt.getString(1);
} finally {
cstmt.close();
}
}
}


运行结果:
Time spend: 674 Per: 0.674
MiceRice 2012-06-20
  • 打赏
  • 举报
回复
我发现你钻进那啥出不来了。。。

所谓预跑,也就是第一次运行的时候需要装载相关的对象,初始化一些static属性。

这些东西,全JVM也就一次。

你的客户如果访问服务端,第一次是90毫秒,以后都是0.6毫秒。

如果你不信,就用JSP去测试。用Apache提供的ab进行压力测试,或者用LoadRunner进行压力测试。压力测试之前先手工访问该JSP一次,后面自然就能看到测试结果了。
ATCG 2012-06-20
  • 打赏
  • 举报
回复
很感谢,我关心的是每次客户端调用的时候,会执行预跑这段代码吗?
客户端调用的时候,程序如果是main函数吗?这样的话,每次都要预跑了?

[Quote=引用 31 楼 的回复:]
预跑这个是JVM级别的,全JVM也就第一次90毫秒,以后都不需要了,你要避免个啥啊?

我又写了个并发测试的,也就是说,只要JVM中曾经有地方执行过一次,其它所有线程再执行都是0.6毫秒。


Java code

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLExcep……
[/Quote]
MiceRice 2012-06-20
  • 打赏
  • 举报
回复
预跑这个是JVM级别的,全JVM也就第一次90毫秒,以后都不需要了,你要避免个啥啊?

我又写了个并发测试的,也就是说,只要JVM中曾经有地方执行过一次,其它所有线程再执行都是0.6毫秒。


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

import oracle.jdbc.OracleCallableStatement;

public class TestCall {
private static int TIMES = 1000;
private static int THREAD_NUM = 100;

private static String URL = "jdbc:oracle:thin:@192.168.234.128:1521:redhat5";
private static String USER = "zenki";
private static String PASS = "zenki";

public static Connection openConnection() throws SQLException {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
return DriverManager.getConnection(URL, USER, PASS);
}

public static void main(String[] args) throws Exception {
Connection con = openConnection();
final String sql = "call proc_test(?)";

// 预跑
SimpleCaller.callProc(con, sql, "");

// 单线程性能测试
long timer = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
SimpleCaller.callProc(con, sql, String.valueOf(i));
}
timer = System.currentTimeMillis() - timer;
System.out.println("\nTime spend: " + timer + "\t Per: " + (1.0 * timer / TIMES));

// 多线程准备
Thread[] threads = new Thread[THREAD_NUM];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread("T" + i) {
public void run() {
try {
Connection con = openConnection();
for (int i = 0; i < TIMES; i++) {
SimpleCaller.callProc(con, sql, String.valueOf(i));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
};
}

// 多线程性能测试
timer = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
timer = System.currentTimeMillis() - timer;
System.out.println("MulitThread done. Time spend: " + timer + "\t Per: " + (1.0 * timer / TIMES / THREAD_NUM)
+ "\t ThreadNum: " + THREAD_NUM);

}
}

class SimpleCaller {
public static String callProc(Connection con, String sql, String param) throws SQLException {
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall(sql);
try {
cstmt.registerOutParameter(1, Types.VARCHAR);
cstmt.setString(1, String.valueOf(param));
cstmt.execute();
return cstmt.getString(1);
} finally {
cstmt.close();
}
}
}


测试结果:
Time spend: 672 Per: 0.672
MulitThread done. Time spend: 20845 Per: 0.20845 ThreadNum: 100
因为我是4核的,所以并发下单次的平均时间会更低。


如果楼主还不信的话,就把Connection简单池化,然后用JSP做性能测试吧。
ATCG 2012-06-20
  • 打赏
  • 举报
回复
噢,不对,ldh911大侠
你的代码里有个“预跑”功能,这个要90豪秒
我说的就是这个 90 豪秒,没法避免。
ATCG 2012-06-20
  • 打赏
  • 举报
回复
多谢,好奇怪
为什么我的OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall(sql);第一次执行要 90豪秒
而你的OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall(sql);
第一次执行也非常快,小于 0.X豪秒

[Quote=引用 28 楼 的回复:]
楼主,你下结论之前,最好自己写代码多测试下吧。

客户端每次传参数进来,每次也就是0.6毫秒:


Java code

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

import oracle.……
[/Quote]
ATCG 2012-06-19
  • 打赏
  • 举报
回复
请教 OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)"); 这一行代码也是可以放在连接池里的吗?
ATCG 2012-06-19
  • 打赏
  • 举报
回复
谢谢你,也就是说,我的代码,单次调用要90豪秒,是包含了prepareCall的时间
而你单次调用并没有包含prepareCall的时间,也就是说 实际真正需要连接的 prepareCall只有一次?

另外的prepareCall都是重用了连接,而prepareCall本身是很快的?
我的prepareCall很慢(90豪秒)是因为每次都要连接?

还想请教一下,为什么我调用一次,打印出的时间有的时候是 16豪秒或者15豪秒,有的时候是 0豪秒?
我有点担心,16豪秒也是不能接受的。

[Quote=引用 16 楼 的回复:]
我1000次测试中,全都是重新生成prepareCall的,总计消耗:671毫秒

for (int i = 0; i < TIMES; i++) {
cstmt.close(); // 关闭
cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)"); // 创建
cstmt.setStrin……
[/Quote]
ATCG 2012-06-19
  • 打赏
  • 举报
回复
晕了,那我不是每次客户端传参数进来,都要prepareCall一次,每次都90豪秒?
这个可不是循环哦,,是客户端一次次的传参数进来,每次都prepareCall,每次90豪秒?

ATCG 2012-06-19
  • 打赏
  • 举报
回复
晕了,那我不是每次都要prepareCall,每次都90豪秒?

[Quote=引用 25 楼 的回复:]
引用 24 楼 的回复:
谢谢,我的意思是
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)");

是否是可以放在连接池的?如果这样的话,就行了


不可以,连接池只池化数据库连接,也就是Connection。

如果要池化CallableSt……
[/Quote]
MiceRice 2012-06-19
  • 打赏
  • 举报
回复
我1000次测试中,全都是重新生成prepareCall的,总计消耗:671毫秒

for (int i = 0; i < TIMES; i++) {
cstmt.close(); // 关闭
cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)"); // 创建
cstmt.setString(1, String.valueOf(i));
cstmt.registerOutParameter(1, Types.VARCHAR);
cstmt.execute();
System.out.print(cstmt.getString(1) + " ");
}

也就是每次实际只消耗0.671毫秒。但这里重用了Connection,因为应用程序都会用连接池来池化数据库连接,所以Connection就没必要每次重新弄了。
ATCG 2012-06-19
  • 打赏
  • 举报
回复
如果不做池化,就比较容易实现?
那么prepareCall本身需要耗时90豪秒,1000次,每次90豪秒?

我因为是在客户端传入参数给存储过程的,这样的循环并不能解决问题

[Quote=引用 14 楼 的回复:]
其实我测试的结果是想说明,即便你进行了池化,每个请求的处理效率大概也只能提升0.1毫秒。

当然如果说0.1毫秒对你来说依然很宝贵的话,那么就确实需要做池化了。
[/Quote]
MiceRice 2012-06-19
  • 打赏
  • 举报
回复
其实我测试的结果是想说明,即便你进行了池化,每个请求的处理效率大概也只能提升0.1毫秒。

当然如果说0.1毫秒对你来说依然很宝贵的话,那么就确实需要做池化了。
ATCG 2012-06-19
  • 打赏
  • 举报
回复
非常感谢,可是和我写的是一样的,我是希望
我就是想达到这样的效果
1、建立连接到数据库
2、prepareCall存储过程
然后程序一直等着,客户端有参数传过来了,就
setString
execute()

就是prepareCall后程序一直等着,只要有参数传过来,就执行存储过程
你这个是循环,我需要的是客户端传入参数N次,只执行setString和execute() prepareCall执行一次后就一直等着客户端传参数,实在不行prepareCall执行多次也行,但是必须保证只有第一次prepareCall慢,第2次后都很快(0.2豪秒)


[Quote=引用 12 楼 的回复:]
我本地做了这个测试,程序和效果如下:


Java code

/**
* CREATE OR REPLACE PROCEDURE proc_test(p_str in out varchar2) AS
* BEGIN
* p_str := 'ECHO: ' || p_str;
* END;
*/
public class TestCall {

……
[/Quote]
MiceRice 2012-06-19
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]
谢谢,我的意思是
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)");

是否是可以放在连接池的?如果这样的话,就行了
[/Quote]

不可以,连接池只池化数据库连接,也就是Connection。

如果要池化CallableStatement,就自己编写,类似于我在20楼提供的 CallablePool 这个类。
ATCG 2012-06-19
  • 打赏
  • 举报
回复
谢谢,我的意思是
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)");

是否是可以放在连接池的?如果这样的话,就行了

[Quote=引用 23 楼 的回复:]
你这个代码,跟我之前的测试代码基本是一致的,第一次创建对象会比较久些,这个很正常,要包含了Class装载初始化等时间,后面就是基准时间了。

所以一般做性能测试,都会把第一次刨除在外,你看我第一次写的代码也是这样:

// 这里就已经创建Callable了
OracleCallableStatement cstmt = (OracleCallableStatement) con.prep……
[/Quote]
MiceRice 2012-06-19
  • 打赏
  • 举报
回复
你这个代码,跟我之前的测试代码基本是一致的,第一次创建对象会比较久些,这个很正常,要包含了Class装载初始化等时间,后面就是基准时间了。

所以一般做性能测试,都会把第一次刨除在外,你看我第一次写的代码也是这样:

// 这里就已经创建Callable了
OracleCallableStatement cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)");
// 然后才开始计时
long s = System.currentTimeMillis();

for (int i = 0; i < TIMES; i++) {
cstmt.close();
cstmt = (OracleCallableStatement) con.prepareCall("call proc_test(?)"); // 每次都重建
cstmt.setString(1, String.valueOf(i));
cstmt.registerOutParameter(1, Types.VARCHAR);
cstmt.execute();
System.out.print(cstmt.getString(1) + " ");
}
加载更多回复(14)

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧