【问题标题】:How to call oracle stored procedure which include user-defined type in java?如何在java中调用包含用户定义类型的oracle存储过程?
【发布时间】: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?

【问题讨论】:

标签: java oracle stored-procedures jdbc


【解决方案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 代码进行哪些更改?我为此提出了一个问题。 stackoverflow.com/questions/12731742/…
  • 我正在运行来自 Eclipse 的 java 代码。但是输入将变为空。我的案例是对象表。我为此开了一个queston。 stackoverflow.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()));
          }
      

      【讨论】:

        猜你喜欢
        • 2017-05-22
        • 2013-12-04
        • 2019-06-18
        • 2017-03-02
        • 1970-01-01
        • 2010-11-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多