java对接oracle存储过程基本知识(附Demo)
目录
- 前言
- 1. 基本知识
- 2. Demo
- 3. 实战
前言
🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF
爬虫神器,无代码爬取,就来:bright.cn
Java基本知识:
- java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
- 【Java项目】实战CRUD的功能整理(持续更新)
1. 基本知识
Java 通过 JDBC 连接 Oracle 数据库并调用存储过程的方式主要有两种:
-
使用 CallableStatement 直接调用存储过程(原生 JDBC)
-
使用 MyBatis 映射存储过程(推荐)
再补充个Sql的基本知识:(sql 参数检查)
查询对应存储过程的输入输出是什么:
SELECT argument_name, data_type, in_out
FROM all_arguments
WHERE object_name = 'xxx'
AND owner = 'YOUR_SCHEMA_NAME';
然后确保 jdbcType 与数据库的参数类型一致,比如:
- VARCHAR2 → jdbcType=VARCHAR
- NUMBER → jdbcType=NUMERIC
- DATE → jdbcType=TIMESTAMP 或 jdbcType=DATE
截图如下:
2. Demo
需要先引入依赖包,否则会出现如下所示:
java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
截图如下:
依赖包如下:
<!-- oracle-->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.8.0.0</version>
</dependency>
不以Mybatis进行解析,纯Demo:
-
加载 Oracle JDBC 驱动:
使用 oracle.jdbc.driver.OracleDriver 进行驱动加载(Spring Boot 可通过 DataSource 自动管理) -
建立数据库连接:
通过DriverManager.getConnection()
或数据源 DataSource 连接数据库 -
创建 CallableStatement:
通过connection.prepareCall("{ call procedure_name(?, ?, ...) }")
创建存储过程调用对象 -
绑定输入、输出参数:
IN 参数:使用setXXX(index, value)
绑定值
OUT 参数:使用registerOutParameter(index, sqlType)
声明输出参数 -
执行存储过程:
使用callableStatement.execute()
执行存储过程
通过getXXX(index)
读取输出参数的返回值 -
关闭连接:
CallableStatement 和 Connection 需要在 finally 块中关闭,避免资源泄露
import java.sql.*;
import java.text.SimpleDateFormat;
public class OracleProcedureCall {
public static void main(String[] args) {
String url = "jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=xxxxx)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=xxxx)))";
String username = "xxx";
String password = "xxx";
Connection conn = null;
CallableStatement cstmt = null;
try {
// 1. 加载驱动(JDBC 4.0 及以上可省略)
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2. 建立数据库连接
conn = DriverManager.getConnection(url, username, password);
// 3. 预编译存储过程,使用命名参数
String procedureCall = "{ call xxxxxx(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) }";
cstmt = conn.prepareCall(procedureCall);
// 4. 设置输入参数
cstmt.setString("xx1", "1");
cstmt.setInt("xx2", 2);
// 处理日期时间
String dateStr = "2025-03-12 04:31:11"; // 格式:yyyy-MM-dd HH:mm:ss
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date parsedDate = dateFormat.parse(dateStr);
Timestamp timestamp = new Timestamp(parsedDate.getTime());
cstmt.setTimestamp("xx3", timestamp);
cstmt.setString("xx4", "4");
cstmt.setString("xx5", "5");
cstmt.setString("xx6", "6");
cstmt.setString("xx7", "7");
// 5. 注册输出参数
cstmt.registerOutParameter("wl_cntr_plac", Types.VARCHAR);
cstmt.registerOutParameter("wl_new_cntr", Types.VARCHAR);
cstmt.registerOutParameter("wl_new_cntr_plac", Types.VARCHAR);
cstmt.registerOutParameter("wl_return", Types.VARCHAR);
// 6. 执行存储过程
cstmt.execute();
// 7. 获取输出参数
String wl_cntr_plac = cstmt.getString("wl_cntr_plac");
String wl_new_cntr = cstmt.getString("wl_new_cntr");
String wl_new_cntr_plac = cstmt.getString("wl_new_cntr_plac");
String wl_return = cstmt.getString("wl_return");
System.out.println("wl_cntr_plac: " + wl_cntr_plac);
System.out.println("wl_new_cntr: " + wl_new_cntr);
System.out.println("wl_new_cntr_plac: " + wl_new_cntr_plac);
System.out.println("wl_return: " + wl_return);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8. 关闭资源
try {
if (cstmt != null) cstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
截图如下:
如果是Mybaits写法,具体如下:
特意隐藏一些数据,以xx进行代替:
<insert id="callExchangeCntrGateProcedure" statementType="CALLABLE">
{ call xxx(
#{xx1,mode=IN,jdbcType=VARCHAR},
#{xx2,mode=IN,jdbcType=NUMERIC},
#{xx3,mode=IN,jdbcType=DATE},
#{xx4,mode=IN,jdbcType=VARCHAR},
#{xx5,mode=IN,jdbcType=VARCHAR},
#{xx6,mode=IN,jdbcType=VARCHAR},
#{xx8,mode=OUT,jdbcType=VARCHAR},
#{xx7,mode=OUT,jdbcType=VARCHAR},
#{xx9,mode=OUT,jdbcType=VARCHAR},
#{xx10,mode=IN,jdbcType=VARCHAR},
#{xx11,mode=OUT,jdbcType=VARCHAR}
) }
</insert>
然后在 Mapper 接口 里这样写:
@Mapper
public interface MyMapper {
void callExchangeCntrGateProcedure(Map<String, Object> params);
}
调用时:
@Autowired
private MyMapper myMapper;
public void executeProcedure() {
Map<String, Object> params = new HashMap<>();
params.put("xx1", "E3051486");
params.put("xx2", 1001);
params.put("xx3", new Timestamp(System.currentTimeMillis()));
params.put("xx4", "SLS");
params.put("xx5", "R");
params.put("xx6", "G05");
params.put("xx7", "C1");
// MyBatis 会自动填充 OUT 参数
myMapper.callExchangeCntrGateProcedure(params);
System.out.println("wl_cntr_plac: " + params.get("wl_cntr_plac"));
System.out.println("wl_new_cntr: " + params.get("wl_new_cntr"));
System.out.println("wl_new_cntr_plac: " + params.get("wl_new_cntr_plac"));
System.out.println("wl_return: " + params.get("wl_return"));
}
方法 | 适用场景 | 主要特点 |
---|---|---|
CallableStatement(命名参数) | 适用于 JDBC 原生代码 | 手动绑定参数,清晰易维护 |
MyBatis #{} 方式 | 适用于 Spring + MyBatis | 代码更简洁,自动管理参数 |
3. 实战
以Demo为基本知识 融合到实战中!
本身需要跨源数据库,所以结合DS注解,该注解可看我之前的文章:详细分析MybatisPLus中@DS切换数据源的基本知识(附Demo)
新建一个基本类,用于跨源数据库
对应实现类打上DS:
实际代码如下:
@Resource
private CabinetSwapMapper cabinetSwapMapper;
@Override
@DS("xxx")
public Map<String, Object> callExchangeCntrGate(CabinetSwapDO cabinetSwapDO, String cntrDam) {
// **调用存储过程**
Map<String, Object> params = new HashMap<>();
params.put("xxx", cabinetSwapDO.getOrderId());
// 省略
params.put("wlChangRsn", cabinetSwapDO.getDamDes());
// 执行存储过程
cabinetSwapMapper.callExchangeCntrGate(params);
return params;
}
此处的存储过程后续便于对接Mabatis.xml文件:
@Mapper
public interface CabinetSwapMapper extends BaseMapperX<CabinetSwapDO> {
void callExchangeCntrGate(Map<String, Object> params);
}
注意的关键点如下:
后续直接调用即可:
Map<String, Object> result = commonGateService.callExchangeCntrGate(cabinetSwapDO, reason);
String xxx= (String) result.get("xxx");
上述代码需要注意IN有什么要传入的,而且参数的字段类型要正确,传输的格式也要正确!
可以先在数据库软件中测试对应的类型: