如何在Java(Hibernate)中调用带自定义类型参数的Oracle存储过程
解决Java+Hibernate调用Oracle包中PL/SQL自定义类型存储过程的问题
针对你遇到的Oracle包内REG_RET记录类型、TAB_RET关联数组类型的参数传递问题,直接利用Oracle JDBC驱动的扩展API结合Hibernate即可解决,具体步骤如下:
1. 准备Java实体类映射PL/SQL记录类型
先创建对应REG_RET的Java类,字段类型与Oracle定义严格匹配:
import java.math.BigDecimal; public class RegRet { private Integer group; private Integer market; private BigDecimal amount; public RegRet(Integer group, Integer market, BigDecimal amount) { this.group = group; this.market = market; this.amount = amount; } // 生成getter方法 public Integer getGroup() { return group; } public Integer getMarket() { return market; } public BigDecimal getAmount() { return amount; } }
2. 结合Hibernate与Oracle JDBC扩展API调用存储过程
Hibernate的StoredProcedureQuery可以直接unwrap到Oracle专属的OracleCallableStatement,利用其setPlsqlIndexTable方法处理PL/SQL关联数组:
import oracle.jdbc.OracleCallableStatement; import oracle.jdbc.OracleConnection; import oracle.sql.STRUCT; import oracle.sql.StructDescriptor; import javax.persistence.EntityManager; import javax.persistence.ParameterMode; import javax.persistence.StoredProcedureQuery; import java.sql.CallableStatement; import java.sql.Connection; import java.util.ArrayList; import java.util.List; import java.math.BigDecimal; // 1. 构造要传入的记录列表 List<RegRet> retList = new ArrayList<>(); retList.add(new RegRet(1, 110, new BigDecimal("500"))); retList.add(new RegRet(1, 95, new BigDecimal("300"))); // 2. 初始化Hibernate存储过程查询 EntityManager em = // 从上下文获取你的EntityManager StoredProcedureQuery query = em.createStoredProcedureQuery("PAC_EXAMPLE.P_CREATE_ELEMENT"); // 3. 注册所有参数 query.registerStoredProcedureParameter(1, String.class, ParameterMode.IN); query.registerStoredProcedureParameter(2, String.class, ParameterMode.IN); query.registerStoredProcedureParameter(3, Object.class, ParameterMode.IN); // 占位,后续用JDBC处理 query.registerStoredProcedureParameter(4, Integer.class, ParameterMode.OUT); // 4. 设置普通输入参数 query.setParameter(1, "JHON"); query.setParameter(2, "JHON R"); // 5. 转换为Oracle专属CallableStatement OracleCallableStatement oracleCs = query.unwrap(OracleCallableStatement.class); try { // 6. 获取Oracle连接,构造STRUCT数组(对应REG_RET记录) Connection conn = oracleCs.getConnection(); OracleConnection oracleConn = conn.unwrap(OracleConnection.class); StructDescriptor structDesc = StructDescriptor.createDescriptor("PAC_EXAMPLE.REG_RET", oracleConn); STRUCT[] structArray = new STRUCT[retList.size()]; for (int i = 0; i < retList.size(); i++) { RegRet ret = retList.get(i); Object[] attributes = new Object[]{ ret.getGroup(), ret.getMarket(), ret.getAmount() }; structArray[i] = new STRUCT(structDesc, oracleConn, attributes); } // 7. 设置PL/SQL关联数组参数 oracleCs.setPlsqlIndexTable( 3, // 参数索引(第三个参数) structArray,// 要传入的STRUCT数组 structArray.length, // 数组长度 structArray.length, // 最大元素数 oracle.jdbc.OracleTypes.STRUCT, // 元素类型 "PAC_EXAMPLE.REG_RET" // 元素对应的PL/SQL类型名称 ); // 8. 执行存储过程并获取输出参数 oracleCs.execute(); Integer outId = oracleCs.getInt(4); System.out.println("output: " + outId); } catch (Exception e) { e.printStackTrace(); } finally { try { oracleCs.close(); } catch (Exception e) { e.printStackTrace(); } }
关键注意事项
- 必须引入Oracle官方JDBC驱动(如
ojdbc8),setPlsqlIndexTable是Oracle专属API; - 确保数据库用户拥有
PAC_EXAMPLE包的执行权限; - 包定义中存在笔误:
PROCEDURE的pa_ret参数类型应为TAB_RET(而非tab_retenciones),END语句应改为END PAC_EXAMPLE;,否则存储过程无法正常编译。
内容的提问来源于stack exchange,提问作者rolo
相关产品推荐
相关产品推荐

