Получить тип таблицы Oracle из хранимой процедуры с помощью JDBC

Я пытаюсь понять различные способы получения табличных данных из хранимых процедур / функций Oracle с помощью JDBC. Шесть способов следующие:

  1. процедура, возвращающая тип таблицы уровня схемы в качестве параметра OUT
  2. процедура, возвращающая тип таблицы уровня пакета в качестве параметра OUT
  3. процедура, возвращающая тип курсора уровня пакета в качестве параметра OUT
  4. функция, возвращающая тип таблицы на уровне схемы
  5. функция, возвращающая тип таблицы уровня пакета
  6. функция, возвращающая тип курсора на уровне пакета

Вот несколько примеров на PL / SQL:

-- schema-level table type
CREATE TYPE t_type AS OBJECT (val VARCHAR(4));
CREATE TYPE t_table AS TABLE OF t_type;

CREATE OR REPLACE PACKAGE t_package AS
  -- package level table type
  TYPE t_table IS TABLE OF some_table%rowtype;
  -- package level cursor type
  TYPE t_cursor IS REF CURSOR;
END library_types;

-- and example procedures:
CREATE PROCEDURE p_1 (result OUT t_table);
CREATE PROCEDURE p_2 (result OUT t_package.t_table);
CREATE PROCEDURE p_3 (result OUT t_package.t_cursor);
CREATE FUNCTION f_4 RETURN t_table;
CREATE FUNCTION f_5 RETURN t_package.t_table;
CREATE FUNCTION f_6 RETURN t_package.t_cursor;

Мне удалось вызвать 3, 4 и 6 с помощью JDBC:

// Not OK: p_1 and p_2
CallableStatement call = connection.prepareCall("{ call p_1(?) }");
call.registerOutParameter(1, OracleTypes.CURSOR);
call.execute(); // Raises PLS-00306. Obviously CURSOR is the wrong type

// OK: p_3
CallableStatement call = connection.prepareCall("{ call p_3(?) }");
call.registerOutParameter(1, OracleTypes.CURSOR);
call.execute();
ResultSet rs = (ResultSet) call.getObject(1); // Cursor results

// OK: f_4
PreparedStatement stmt = connection.prepareStatement("select * from table(f_4)");
ResultSet rs = stmt.executeQuery();

// Not OK: f_5
PreparedStatement stmt = connection.prepareStatement("select * from table(f_5)");
stmt.executeQuery(); // Raises ORA-00902: Invalid data type

// OK: f_6
CallableStatement call = connection.prepareCall("{ ? = call f_6 }");
call.registerOutParameter(1, OracleTypes.CURSOR);
call.execute();
ResultSet rs = (ResultSet) call.getObject(1); // Cursor results

Очевидно, у меня проблемы с пониманием

  1. Как получить типы таблиц уровня схемы и уровня пакета из параметров OUT в хранимых процедурах
  2. Как получить типы таблиц уровня пакета из сохраненных функций

Я не могу найти никакой документации по этому поводу, так как все всегда используют курсоры вместо типов таблиц. Может потому что это невозможно? Однако я предпочитаю типы таблиц, потому что они формально определены и могут быть обнаружены с помощью представлений словаря (по крайней мере, типов таблиц уровня схемы).

Примечание: очевидно, я мог бы написать функцию-оболочку, возвращающую параметры OUT и типы таблиц уровня пакета. Но я бы предпочел чистое решение.


person Lukas Eder    schedule 20.06.2011    source источник
comment
Ожидаете ли вы, что результатом будет структура таблицы, то есть column1 varchar2 (100), column2 varchar2 (50) ...? или вы хотите увидеть "IS REF CURSOR" или что-нибудь в этом роде?   -  person Joel Slowik    schedule 20.06.2011
comment
Чем больше информации о типе курсора / таблицы я получу, тем лучше. Но в любом случае, я думаю, что с JDBC ResultSet.getMetaData() я могу найти эту информацию на лету.   -  person Lukas Eder    schedule 20.06.2011
comment
@LukasEder Как насчет mysql? Мне нужно вызвать процедуру в java, используя jdbc, которая возвращает таблицу ... возможно ли через jdbc с использованием mysql?   -  person Sarah Akhavan    schedule 23.06.2016
comment
@ Джек из коробки: Пожалуйста, задайте новый вопрос. Это очень конкретный вопрос, связанный с Oracle   -  person Lukas Eder    schedule 24.06.2016
comment
@LukasEder Спасибо. Я спросил это на днях :) здесь   -  person Sarah Akhavan    schedule 25.06.2016


Ответы (2)


Вы не можете получить доступ к объектам PLSQL (случаи 2 и 5 = объекты уровня пакета) из java, см. "java - передача массива в сохраненном оракуле процедура ». Однако вы можете получить доступ к типам SQL (случай 1 и 4).

Чтобы получить параметры OUT из PL / SQL в java, вы можете использовать метод, описанный в один из потоков Тома Кайта с использованием OracleCallableStatement. В вашем коде будет дополнительный шаг, поскольку вы получаете таблицу Object вместо таблицы VARCHAR.

Вот демонстрация использования таблицы объекта SQL, сначала настройка:

SQL> CREATE TYPE t_type AS OBJECT (val VARCHAR(4));
  2  /
Type created

SQL> CREATE TYPE t_table AS TABLE OF t_type;
  2  /
Type created

SQL> CREATE OR REPLACE PROCEDURE p_sql_type (p_out OUT t_table) IS
  2  BEGIN
  3     p_out := t_table(t_type('a'), t_type('b'));
  4  END;
  5  /
Procedure created

Фактический класс java (с использованием dbms_output.put_line для регистрации, потому что я буду вызывать его из SQL, используйте System.out.println, если вызывается из java):

SQL> CREATE OR REPLACE
  2  AND COMPILE JAVA SOURCE NAMED "ArrayDemo"
  3  as
  4  import java.sql.*;
  5  import oracle.sql.*;
  6  import oracle.jdbc.driver.*;
  7  
  8  public class ArrayDemo {
  9     
 10     private static void log(String s) throws SQLException {
 11        PreparedStatement ps =
 12           new OracleDriver().defaultConnection().prepareStatement
 13           ( "begin dbms_output.put_line(:x); end;" );
 14        ps.setString(1, s);
 15        ps.execute();
 16        ps.close();
 17     }
 18  
 19     public static void getArray() throws SQLException {
 20  
 21        Connection conn = new OracleDriver().defaultConnection();
 22  
 23        OracleCallableStatement cs =
 24           (OracleCallableStatement)conn.prepareCall
 25           ( "begin p_sql_type(?); end;" );
 26        cs.registerOutParameter(1, OracleTypes.ARRAY, "T_TABLE");
 27        cs.execute();
 28        ARRAY array_to_pass = cs.getARRAY(1);
 29  
 30        /*showing content*/
 31        Datum[] elements = array_to_pass.getOracleArray();
 32  
 33        for (int i=0;i<elements.length;i++){
 34           Object[] element = ((STRUCT) elements[i]).getAttributes();
 35           String value = (String)element[0];
 36           log("array(" + i + ").val=" + value);
 37        }
 38     }
 39  }
 40  /
Java created

Назовем это:

SQL> CREATE OR REPLACE
  2  PROCEDURE show_java_calling_plsql
  3  AS LANGUAGE JAVA
  4  NAME 'ArrayDemo.getArray()';
  5  /

Procedure created

SQL> EXEC show_java_calling_plsql;

array(0).val=a
array(1).val=b
person Vincent Malgrat    schedule 20.06.2011
comment
Спасибо за ваш ответ! Я подумал, что это может быть связано с типом ARRAY, поэтому попробую то, что вы предлагаете. - person Lukas Eder; 20.06.2011
comment
Ах, глупый я. Единственное, что я забыл, это указать имя типа ARRAY при регистрации параметра OUT. - person Lukas Eder; 20.06.2011
comment
Однако меня все еще беспокоит то, что я могу получить доступ к типу REF CURSOR уровня пакета. Я думаю, что невозможность доступа к TABLE типам на уровне пакета - это просто недостаток реализации драйвера JDBC ... - person Lukas Eder; 20.06.2011
comment
Я думаю, вы можете получить доступ к типу курсора ref, потому что он общий (все курсоры ref одинаковы). SQL также не может получить доступ к типам PL / SQL (например, ваш запрос № 5 не работает в SQL * Plus), поэтому я не уверен, является ли неспособность jdbc доступа к этим типам недостатком или существует техническое ограничение ( Типы PL / SQL по какой-то причине являются частными?). - person Vincent Malgrat; 20.06.2011
comment
Ты прав. Я все время забываю, что это не просто ограничение Java / JDBC ... ах, как бы мне хотелось, чтобы это было проще! Спасибо, в любом случае! - person Lukas Eder; 20.06.2011
comment
@Lukas в последних версиях базы данных (10g?) Вы можете получить доступ из таблиц уровня пакета SQL, возвращаемых функциями, если функции объявлены как PIPELINED. Можете ли вы использовать конвейерные функции вместо обычных? - person Vincent Malgrat; 20.06.2011
comment
Хорошо, я не знал о ключевом слове PIPELINED. Я могу использовать это в своей тестовой базе данных. - person Lukas Eder; 20.06.2011

Вы также можете использовать нижеприведенный

public List<EmployeeBean> fetchDataFromSPForRM(String sInputDate) {

         List<EmployeeBean> employeeList = new ArrayList<EmployeeBean>();

         Connection dbCon = null;
         ResultSet data = null;
         CallableStatement cstmt = null;


         try {
                dbCon = DBUtil.getDBConnection();
                String sqlQuery = "{? = call PKG_HOLD_RELEASE.FN_RM_PDD_LIST()}";

                cstmt = dbCon.prepareCall(sqlQuery);

                cstmt.registerOutParameter(1, OracleTypes.CURSOR);

                cstmt.execute();

                data = (ResultSet) cstmt.getObject(1);              

                    while(data.next()){
                        EmployeeBean employee = new EmployeeBean();

                        employee.setEmpID(data.getString(1));
                        employee.setSubBusinessUnitId((Integer)data.getObject(2));
                        employee.setMonthOfIncentive((Integer)data.getObject(3));
                        employee.setPIPStatus(data.getString(5));
                        employee.setInvestigationStatus(data.getString(6));
                        employee.setEmpStatus(data.getString(7));
                        employee.setPortfolioPercentage((Integer)data.getObject(8));
                        employee.setIncentive((Double)data.getObject(9));
                        employee.setTotalSysemHoldAmt((Double)data.getObject(10));
                        employee.setTotalManualHoldAmt((Double)data.getObject(11));

                        employeeList.add(employee);
                    }

            } catch (SQLException e) {
                e.printStackTrace();
            }finally{
                try {

                    if(data != null){

                            data.close();               
                            data = null;
                    }
                    if(cstmt != null){

                        cstmt.close();
                        cstmt = null;
                    }
                    if(dbCon != null){

                            dbCon.close();              
                            dbCon = null;
                    }

                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }


        return employeeList;                
     }
person Tanmay kumar shaw    schedule 25.03.2015
comment
Спасибо. Вопрос не в этом. Да, вы можете материализовать (ref) курсор, который возвращается из хранимой процедуры. Вопрос заключался в том, какие типы курсорных типов PL / SQL вообще могут быть материализованы, в частности: могут ли быть материализованы типы коллекций PL / SQL. - person Lukas Eder; 26.03.2015