如何在java中调用包含用户定义类型的oracle存储过程?
Posted
技术标签:
【中文标题】如何在java中调用包含用户定义类型的oracle存储过程?【英文标题】:How to call oracle stored procedure which include user-defined type in java? 【发布时间】:2011-04-07 06:21:59 【问题描述】:在 Oracle 数据库中:
我有以下存储过程:
procedure getInfo ( p_ids IN IDS_TABLE, p_details OUT cursor )
键入IDS_TABLE
是:
create or replace type IDS_TABLE as table of IDS
create or replace type IDS as object ( id1 NUMBER, id2 NUMBER, id3 NUMBER )
如何在 Java 中调用 getInfo?
【问题讨论】:
类似于Read an ARRAY from a STRUCT returned by a stored procedure 【参考方案1】:手动设置 Oracle SQL 对象和 java 对象之间的链接并非易事。特别是,用户定义对象的数组(或嵌套表)从 java 传递到 Oracle 比标准数据类型的数组更复杂。换句话说,调用带有签名的过程更容易:
(TABLE OF NUMBER, TABLE OF NUMBER, TABLE OF NUMBER)`
比一个签名为:
(TABLE OF (NUMBER, NUMBER, NUMBER)) <- your case
您可以为您的过程编写一个包装器,将第二种情况转换为第一种情况。
话虽如此,到目前为止,映射您的过程并非不可能。以下示例为largely inspired by a post by Tom Kyte。 Tom 描述了如何使用oracle.sql.ARRAY
映射TABLE OF NUMBER
。在您的情况下,我们还必须使用 oracle.sql.STRUCT
来映射 IDS
SQL 对象。
您可能还想浏览 Oracle JDBC 文档,尤其是章节 Working with Oracle Object Types。
首先是一个和你类似的设置:
SQL> CREATE OR REPLACE TYPE IDS AS OBJECT ( id1 NUMBER, id2 NUMBER, id3 NUMBER );
2 /
Type created
SQL> CREATE OR REPLACE TYPE IDS_TABLE AS TABLE OF IDS;
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE getInfo(p_ids IN IDS_TABLE) IS
2 BEGIN
3 FOR i IN 1 .. p_ids.COUNT LOOP
4 dbms_output.put_line(p_ids(i).id1
5 || ',' || p_ids(i).id2
6 || ',' || p_ids(i).id3);
7 END LOOP;
8 END getInfo;
9 /
Procedure created
这是java程序:
SQL> CREATE OR REPLACE
2 AND COMPILE JAVA SOURCE NAMED "ArrayDemo"
3 as
4 import java.io.*;
5 import java.sql.*;
6 import oracle.sql.*;
7 import oracle.jdbc.driver.*;
8
9 public class ArrayDemo
10
11 public static void passArray() throws SQLException
12
13 Connection conn =
14 new OracleDriver().defaultConnection();
15
16
17 StructDescriptor itemDescriptor =
18 StructDescriptor.createDescriptor("IDS",conn);
19
20 Object[] itemAtributes = new Object[] new Integer(1),
21 new Integer(2),
22 new Integer(3);
23 STRUCT itemObject1 = new STRUCT(itemDescriptor,conn,itemAtributes);
24
25 itemAtributes = new Object[] new Integer(4),
26 new Integer(5),
27 new Integer(6);
28 STRUCT itemObject2 = new STRUCT(itemDescriptor,conn,itemAtributes);
29
30 STRUCT[] idsArray = itemObject1,itemObject2;
31
32 ArrayDescriptor descriptor =
33 ArrayDescriptor.createDescriptor( "IDS_TABLE", conn );
34
35 ARRAY array_to_pass =
36 new ARRAY( descriptor, conn, idsArray );
37
38 OraclePreparedStatement ps =
39 (OraclePreparedStatement)conn.prepareStatement
40 ( "begin getInfo(:x); end;" );
41
42 ps.setARRAY( 1, array_to_pass );
43 ps.execute();
44
45
46
47 /
Java created
我们称之为:
SQL> CREATE OR REPLACE
2 PROCEDURE show_java_calling_plsql
3 AS LANGUAGE JAVA
4 NAME 'ArrayDemo.passArray()';
5 /
Procedure created
SQL> exec show_java_calling_plsql ;
1,2,3
4,5,6
PL/SQL procedure successfully completed
【讨论】:
如果我要更改关联数组的表声明,即create or replace type IDS_TABLE as table of IDS index by varchar2(50)
,我需要对 java 代码进行哪些更改?我为此提出了一个问题。 ***.com/questions/12731742/…
我正在运行来自 Eclipse 的 java 代码。但是输入将变为空。我的案例是对象表。我为此开了一个queston。 ***.com/questions/20099905/…
伟大的 +1,像魅力一样工作。
@Vincet Malgrat 如果将类型 IDS 创建或替换为对象(id1 NUMBER、id2 NUMBER、id3 NUMBER)再次包含一个数组,如创建或替换类型 IDS 作为对象(IDS_query IDS_QUERY_TYPE)并且 IDS_QUERY_TYPE 再次是定义为 CREATE OR REPLACE TYPE IDS_QUERY_TYPE AS TABLE OF varchar2(100) 那么如何定义结构?请帮忙
@NikhilAgrawal 您将使用 Array 定义 IDS_QUERY_TYPE
,然后使用 Struct(将前面的数组作为元素)定义 IDS
,然后使用 Array(将前面的 Struct 作为元素)定义IDS_TABLE
。见this example on pastebin。请注意,STRUCT
constructor 在较新版本的 java 中已弃用,如果您有 java 1.6 或更高版本,请改用Connection.createStruct
。【参考方案2】:
如果您使用的是 Spring,您可能需要查看Spring Data JDBC Extensions,它提供了SqlArrayValue
类型。
7.2.1 Setting ARRAY values using SqlArrayValue for an IN parameter 章解释了如何使用数组参数调用过程。
【讨论】:
【参考方案3】:这是一个很好的例子。如果您仍然看到 java.sql.SQLException: invalid name pattern:。检查您在 Oracle 中声明的类型的范围。我正在使用 Oracle 11g,并且必须在模式级别声明我的类型的字符串数组对象和对象表。花了大约 3 个小时才发现。
oracle.sql.StructDescriptor docObjDescriptor = StructDescriptor.createDescriptor("SSIADM.DOCUMENT_OBJECT",conn);
String[] strArray = new String[] "doc1","file1";
oracle.sql.STRUCT DocObject1 = new STRUCT(docObjDescriptor,conn,strArray);
strArray = new String[] "doc2","file2";
oracle.sql.STRUCT DocObject2 = new STRUCT(docObjDescriptor,conn,strArray);
oracle.sql.STRUCT[] docObjArray = DocObject1,DocObject2;
arrDesc = ArrayDescriptor.createDescriptor("DOCUMENT_TABLE", conn);
oracle.sql.ARRAY array = new ARRAY(arrDesc, conn, docObjArray);
【讨论】:
【参考方案4】:我使用的解决方案让 Spring 解析对象,而不必手动创建 STRUCT 数组。 不幸的是,它仍然不是环境独立的。
存储过程 DAO:
package ****.dao.storedProcedures;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.object.StoredProcedure;
import ****.persistent.ComplexTypeObj;
import ****.persistent.InnerType;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
public class SaveStoredProc extends StoredProcedure implements InitializingBean
public static final String IT_COMPLEX_TYPE = "it_complex_type";
public SaveStoredProc()
@Override
public void afterPropertiesSet()
Validate.notNull(getJdbcTemplate());
super.setFunction(true);
super.declareParameter(new SqlOutParameter(RESULT, Types.NUMERIC));
super.declareParameter(new SqlParameter(IT_COMPLEX_TYPE, Types.OTHER, ComplexTypeObj.ORACLE_OBJECT_NAME));
compile();
public long execute(final ComplexTypeObj complexTypeObj)
Map<String, Object> inParameters = new HashMap<String, Object>();
inParameters.put(IT_COMPLEX_TYPE, new ComplexSqlTypeValue(complexTypeObj));
@SuppressWarnings("unchecked")
Map<String, Object> resp = super.execute(inParameters);
return ((Number)resp.get(RESULT)).longValue();
private static final class ComplexSqlTypeValue implements SqlTypeValue
private final Log logger = LogFactory.getLog(getClass());
private final ComplexTypeObj complexTypeObj;
public ComplexSqlTypeValue(ComplexTypeObj complexTypeObj)
this.complexTypeObj = complexTypeObj;
@Override
public void setTypeValue(PreparedStatement ps, int paramIndex, int sqlType, String typeName) throws SQLException
Connection conn = ps.getConnection();
try
conn = conn.unwrap(oracle.jdbc.OracleConnection.class);
catch (Exception e)
logger.debug("Could not unrap connection");
Map<String, Class<?>> typeMap = conn.getTypeMap();
typeMap.put(typeName, ComplexTypeObj.class); //The name of the outer object type.
typeMap.put(InnerType.ORACLE_OBJECT_NAME, InnerType.class); //The name of the inner object type.
ArrayDescriptor des = ArrayDescriptor.createDescriptor(InnerType.ORACLE_LIST_NAME, conn); //The name of the inner list type.
Array objArray = new ARRAY(des, conn, complexTypeObj.getInnerList().toArray());
complexTypeObj.setInnerArray(objArray);
ps.setObject(paramIndex, complexTypeObj);
外型:
import java.sql.*;
import java.util.*;
public class OuterType extends BaseSQLData implements SQLData
public static final String ORACLE_OBJECT_NAME = "T_OUTER_TYPE";
private List<InnerType> innerList;
private Array innerArray;
public OuterType()
this.innerList = new ArrayList<InnerType>();
public String getSQLTypeName() throws SQLException
return ORACLE_OBJECT_NAME;
@Override
public void writeSQL(SQLOutput stream) throws SQLException
stream.writeArray(innerArray);
内部类型:
public final class InnerType extends BaseSQLData
public static final String ORACLE_OBJECT_NAME = "T_INNER_TYPE";
public static final String ORACLE_LIST_NAME = "T_INNER_TYPE_LIST";
private String valueA;
private Long valueB = 0;
public String getSQLTypeName() throws SQLException
return ORACLE_OBJECT_NAME;
@Override
public void readSQL(SQLInput stream, String typeName) throws SQLException
throw new UnsupportedOperationException("This class doesn't support read opperations.");
@Override
public void writeSQL(SQLOutput stream) throws SQLException
stream.writeString(valueA);
stream.writeBigDecimal(valueB == null ? null : new BigDecimal(valueB.toString()));
【讨论】:
以上是关于如何在java中调用包含用户定义类型的oracle存储过程?的主要内容,如果未能解决你的问题,请参考以下文章