it-swarm.com.de

Wie rufe ich eine gespeicherte Oracle-Prozedur auf, die einen benutzerdefinierten Typ in Java enthält?

In Oracle DB:

Ich habe die folgende gespeicherte Prozedur:

procedure getInfo ( p_ids IN IDS_TABLE, p_details OUT cursor )

Typ IDS_TABLE lautet:

create or replace type IDS_TABLE as table of IDS    

create or replace type IDS as object ( id1 NUMBER, id2 NUMBER, id3 NUMBER )

Wie kann ich getInfo in Java aufrufen?

26
xmurobi

Das manuelle Einrichten einer Verknüpfung zwischen Oracle SQL-Objekten und Java-Objekten ist keine triviale Aufgabe. Insbesondere Arrays (oder geschachtelte Tabellen) von benutzerdefinierten Objekten sind komplexer für die Weitergabe von Java an Oracle als Arrays von Standarddatentypen. Mit anderen Worten, es ist einfacher, eine Prozedur mit Signatur aufzurufen:

(TABLE OF NUMBER, TABLE OF NUMBER, TABLE OF NUMBER)`

als ein Verfahren, dessen Signatur lautet:

(TABLE OF (NUMBER, NUMBER, NUMBER))   <- your case

Sie können einen Wrapper für Ihre Prozedur schreiben, um den zweiten Fall in den ersten Fall umzuwandeln.


Es ist jedoch keineswegs unmöglich, Ihre Prozedur abzubilden. Das folgende Beispiel ist weitgehend von einem Beitrag von Tom Kyte inspiriert . Tom beschreibt, wie ein TABLE OF NUMBER mithilfe von Oracle.sql.ARRAY zugeordnet wird. In Ihrem Fall müssen wir auch Oracle.sql.STRUCT verwenden, um das IDS SQL-Objekt abzubilden.

Sie können auch das Oracle JDBC-Dokument durchsuchen, insbesondere das Kapitel Mit Oracle-Objekttypen arbeiten .

Zuerst ist ein Setup ähnlich wie bei Ihnen:

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

Dies ist die Java-Prozedur:

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

Nennen wir es:

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
35
Vincent Malgrat

Wenn Sie Spring verwenden, möchten Sie vielleicht Spring Data-JDBC-Erweiterungen betrachten, das einen SqlArrayValue-Typ bereitstellt.

Kapitel 7.2.1 Festlegen von ARRAY-Werten mit SqlArrayValue für einen IN-Parameter erläutert, wie Prozeduren mit Array-Parametern aufgerufen werden.

1
Arlo

Bei der von mir verwendeten Lösung kann Spring das Objekt analysieren, anstatt die STRUCT-Arrays manuell erstellen zu müssen ..__ Leider ist es immer noch nicht von der Umgebung abhängig.

Gespeichertes Proc-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);
        }
    }
}

Äußerer Typ:

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);
    }

Innentyp:

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()));
    }
0
ScrappyDev

Dies ist ein ziemlich gutes Beispiel. wenn Sie Java.sql.SQLException sehen: ungültiges Namensmuster: noch. Überprüfen Sie den Gültigkeitsbereich des in Oracle deklarierten Typs. Ich verwende Oracle 11g und musste sowohl Object of String Array als auch Table of Objects meines Typs auf Schemaebene deklarieren. Verbrachte etwa 3 Stunden und fand das.

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);
0