如何通过Hibernate调用带数组参数的Oracle函数执行更新
解决Oracle函数
UNBIND调用的ORA-06576错误问题 首先咱们先捋清楚报错的根源:你定义的UNBIND是带返回值的Oracle函数,但之前的调用方式把它当成了无返回值的存储过程,再加上自定义数组类型的参数没处理到位,才触发了ORA-06576错误。
错误原因拆解
- 用
call UNBIND(:ids):Oracle里CALL主要用来调用存储过程,函数必须接收返回值,直接这么写会让Oracle认为你调用的对象类型不匹配。 - 用
execute UNBIND(:ids):EXECUTE是SQL*Plus的专属命令,不属于标准SQL语法,Hibernate的createSQLQuery根本不支持这种写法。
正确的调用方式
方式1:用SELECT ... FROM DUAL调用函数(推荐)
因为Oracle函数必须返回值,用标准SELECT语句调用是最稳妥的方式,同时要正确处理自定义数组类型id_table的参数传递:
// 1. 编写调用函数的SQL String sql = "SELECT UNBIND(:ids) FROM DUAL"; Query query = getSession().createSQLQuery(sql); // 2. 处理自定义数组参数:把Java集合转成Oracle Array类型 query.setParameter("ids", myIds, new UserType() { @Override public int[] sqlTypes() { return new int[]{OracleTypes.ARRAY}; } @Override public Class<?> returnedClass() { return List.class; // 对应你存储ID的Java类型 } @Override public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { // 不需要获取返回值内容的话,直接返回null即可 return null; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { // 获取数据库连接,创建Oracle Array对象 Connection conn = st.getConnection(); // 注意这里的"ID_TABLE"要和Oracle中定义的数组类型名完全匹配(Oracle默认大写) Array oracleArray = conn.createArrayOf("ID_TABLE", ((List<?>) value).toArray()); st.setArray(index, oracleArray); } // 以下是UserType接口的默认实现,直接照搬即可 @Override public Object deepCopy(Object value) throws HibernateException { return value; } @Override public boolean isMutable() { return false; } @Override public Serializable disassemble(Object value) throws HibernateException { return (Serializable) value; } @Override public Object assemble(Serializable cached, Object owner) throws HibernateException { return cached; } @Override public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; } @Override public boolean equals(Object x, Object y) throws HibernateException { return Objects.equals(x, y); } @Override public int hashCode(Object x) throws HibernateException { return x != null ? x.hashCode() : 0; } }); // 3. 执行查询并获取返回值(函数必须触发查询操作,不能直接用executeUpdate) query.uniqueResult();
方式2:用带返回值的CALL语句
如果更习惯CALL语法,需要注册输出参数来接收函数的返回值:
String sql = "{? = call UNBIND(:ids)}"; CallableStatement callableStmt = getSession().doReturningWork(connection -> { CallableStatement cs = connection.prepareCall(sql); // 注册输出参数:根据你的函数返回值类型调整,比如NUMBER、VARCHAR等 cs.registerOutParameter(1, OracleTypes.NUMBER); // 设置输入参数:转成Oracle Array类型 Array oracleArray = connection.createArrayOf("ID_TABLE", myIds.toArray()); cs.setArray("ids", oracleArray); return cs; }); callableStmt.execute(); // 获取函数返回值 Object result = callableStmt.getObject(1); callableStmt.close();
关键注意事项
- 自定义类型名要匹配:创建Oracle Array时,
createArrayOf的第一个参数必须和你在Oracle中定义的数组类型名(id_table)完全一致,注意Oracle默认是大写的。 - 必须处理返回值:不管用哪种方式,都要接收函数的返回值,不能像调用存储过程那样直接执行更新。
- Hibernate版本兼容:如果你的Hibernate版本较新,可以用
Session.createNativeQuery替代createSQLQuery,用法基本一致。
内容的提问来源于stack exchange,提问作者kamil.rak
相关产品推荐
相关产品推荐

