programing

자바에서 사용자 정의 유형을 포함하는 오라클 저장 프로시저를 어떻게 호출합니까?

muds 2023. 6. 15. 22:09
반응형

자바에서 사용자 정의 유형을 포함하는 오라클 저장 프로시저를 어떻게 호출합니까?

Oracle DB의 경우:

다음과 같은 저장 프로시저가 있습니다.

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 )

자바에서 getInfo를 어떻게 호출합니까?

Oracle SQL 개체와 Java 개체 간의 링크를 수동으로 설정하는 것은 간단한 작업이 아닙니다.특히 사용자 정의 개체의 배열(또는 중첩된 테이블)은 표준 데이터 유형의 배열보다 Java에서 Oracle로 전달하기가 더 복잡합니다.즉, 서명이 있는 프로시저를 호출하는 것이 더 쉽습니다.

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

서명이 다음과 같은 절차보다 더 중요합니다.

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

절차 주위에 래퍼를 작성하여 두 번째 사례를 첫 번째 사례로 변환할 수 있습니다.


그렇긴 하지만, 당신의 절차를 지도화하는 것은 결코 불가능하지 않습니다.다음 예시는 주로 Tom Kyte의 게시물에서 영감을 받았습니다.Tom은 지도를 그리는 방법을 설명합니다.TABLE OF NUMBER사용.oracle.sql.ARRAY당신의 경우에는 우리도 사용해야 할 것입니다.oracle.sql.STRUCT지도를 그리다IDSSQL 개체입니다.

또한 Oracle JDBC 문서, 특히 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

Spring을 사용하는 경우 Spring Data JDBC Extensions를 사용하여 다음을 제공할 수 있습니다.SqlArrayValue유형.

7.2.1 IN 매개 변수에 대해 SqlArrayValue를 사용하여 ARARY 값을 설정하는 방법은 배열 매개 변수를 사용하여 프로시저를 호출하는 방법을 설명합니다.

이것은 꽤 좋은 예입니다.java.sql이 표시되는 경우.SQL 예외: 잘못된 이름 패턴: 여전히.Oracle에서 선언한 유형의 범위를 확인합니다.Oracle 11g을 사용하고 있으며 스키마 레벨에서 내 유형의 Object of String Array와 Table of Objects를 모두 선언해야 했습니다.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);

내가 사용한 솔루션을 사용하면 Spring이 STOR 구조 배열을 수동으로 생성할 필요 없이 객체를 구문 분석할 수 있습니다.불행하게도, 그것은 여전히 환경에 독립적이지 않습니다.

저장된 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);
        }
    }
}

외부 유형:

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

언급URL : https://stackoverflow.com/questions/3626061/how-to-call-oracle-stored-procedure-which-include-user-defined-type-in-java

반응형