一个JDBC驱动的陷阱

近期公司的项目在升级Oracle 12C,除了要替换wm_concat之类的不能用的函数之类的预料中的问题外,我还遇到了一个挺奇怪的JDBC驱动问题:


就是使用JDBC调用存储过程时,发现获取的CallableStatement接口对象的setObject方法,用“命名参数”时有问题。


为了描述方便,我写成了一个小DEMO。


首先,有一个存储过程

CREATE OR REPLACE PROCEDURE PRINT_USER
(
       USERID        IN  VARCHAR2,
       USERNAME      IN  VARCHAR2,
       PRINTINFO     OUT VARCHAR2
)
AS
BEGIN
      PRINTINFO := '您的   用户ID是:['||USERID||']    用户名是:['||USERNAME||']';
EXCEPTION
  WHEN OTHERS THEN
    PRINTINFO := SQLCODE||'-'||SQLERRM;
END;


然后,编写一个简单的Java程序来调用一下它,并打印出它的出参。

package com.dullchap.db.driverproblem;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class TestDao {

	static Connection 		 conn;
	static ResultSet 		 rs;
	static PreparedStatement stmt;
	
	public static void main(String[] args) throws Exception{
		try {
			//加载驱动,获取链接
			Class.forName("oracle.jdbc.driver.OracleDriver");
			conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl1", "scott", "scott");
			
			//获取存储过程执行对象
			CallableStatement lcs_state = conn.prepareCall("BEGIN PRINT_USER(:USERID,:USERNAME,:PRINTINFO); END;");
			
			//设置存储过程出/入参数
			lcs_state.setObject("USERNAME", "张三");
			lcs_state.setObject("USERID", "123");
			lcs_state.registerOutParameter("PRINTINFO", java.sql.Types.VARCHAR);
			
			//执行存储过程
			lcs_state.executeUpdate();
			
			//打印存储过程结果
			System.out.println(lcs_state.getObject("PRINTINFO"));
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//释放连接
			if (rs != null)		rs.close();
			if (stmt != null)	stmt.close();
			if (conn != null)	conn.close();
		}
	}
}

未升级Oracle 12C前,执行结果当然是没问题的,如下:

您的   用户ID是:[123]    用户名是:[张三]



但升级了Oracle 12C,并使用新的jdbc驱动包(ojdbc7.jar)时,问题来了,发现打印结果如下:

您的   用户ID是:[张三]    用户名是:[123]


经调查之后,发现ojdbc7.jar完全忽略了CallableStatement对应对象的setObject方法的第一个参数,


它完全按照先后顺序来给存储过程入参赋值了。


也就是说,我如果把代码的setObject那两行交换一下顺序,程序的输出也是正常的。


我在Oracle官网也查看了一下,有提到setObject(String,Object)是不推荐使用的方法了。还是老老实实用回setObject(int,Ojbect),乖乖写问号吧。

package com.dullchap.db.driverproblem;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class TestDao {

	static Connection 		 conn;
	static ResultSet 		 rs;
	static PreparedStatement stmt;
	
	public static void main(String[] args) throws Exception{
		try {
			//加载驱动,获取链接
			Class.forName("oracle.jdbc.driver.OracleDriver");
			conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl1", "scott", "scott");
			
			//获取存储过程执行对象
//			CallableStatement lcs_state = conn.prepareCall("BEGIN PRINT_USER(:USERID,:USERNAME,:PRINTINFO); END;");
			CallableStatement lcs_state = conn.prepareCall("BEGIN PRINT_USER(?,?,?); END;");
			
			//设置存储过程出/入参数
//			lcs_state.setObject("USERNAME", "张三");
//			lcs_state.setObject("USERID", "123");
//			lcs_state.registerOutParameter("PRINTINFO", java.sql.Types.VARCHAR);
			lcs_state.setObject(2, "张三");
			lcs_state.setObject(1, "123");
			lcs_state.registerOutParameter(3, java.sql.Types.VARCHAR);
			
			//执行存储过程
			lcs_state.executeUpdate();
			
			//打印存储过程结果
//			System.out.println(lcs_state.getObject("PRINTINFO"));
			System.out.println(lcs_state.getObject(3));
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			//释放连接
			if (rs != null)		rs.close();
			if (stmt != null)	stmt.close();
			if (conn != null)	conn.close();
		}
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值