Arhn - архитектура программирования

Передать массив ColdFusion в коллекцию Oracle в качестве переменной связывания

Учитывая хранимую процедуру Oracle:

CREATE TYPE stringlist AS TABLE OF VARCHAR2(100);
/

CREATE PROCEDURE test_proc(
  list   IN  stringlist,
  output OUT VARCHAR2
)
AS
BEGIN
  IF list IS NULL OR list IS EMPTY THEN
    RETURN;
  END IF;
  output := list(1);
  FOR i IN 2 .. list.COUNT LOOP
    output := output || ',' || list(i);
  END LOOP;
END;
/

Как я могу вызвать это из ColdFusion?

<cfscript>
  arr = [ 'A', 'B', 'C' ];

  sp = new StoredProc(
    dataSource = "orcl",
    procedure  = "test_proc",
    result     = "NA",
    parameters = [
      { cfsqltype = "CF_SQL_ARRAY",  type="in",   value = arr },
      { cfsqltype = "CF_SQL_VARCHAR", type="out", variable = "out" }
    ]
  ).execute();

  // WriteDump( sp.getProcOutVariables().out );
</cfscript>

Не удается с:

Error Executing Database Query
Fail to convert to internal representation: [A, B, C]
12.05.2016

  • Почему аргумент не может быть списком? 12.05.2016
  • Я знаю, что это можно сделать, преобразовав массив в строку, а затем удалив его на другом конце, но тогда у вас есть два преобразования, и вам нужно изменить хранимую процедуру. Но в основном потому, что вопрос был задан не об этом, а конкретно о передаче массива в коллекцию - что-то, что можно сделать на Java но я не нашел простого/нативного способа в ColdFusion. 12.05.2016

Ответы:


1

Во-первых, настройте источник данных, который использует JDBC-драйверы Oracle< /а>. Загрузите соответствующий файл JAR и поместите его в каталог lib экземпляра coldfusion, а затем через панель администрирования CFIDE вы можете настроить источник данных следующим образом:

CF Data Source Name: orcl
JDBC URL:            jdbc:oracle:thin:@localhost:1521:orcl
Driver Class:        oracle.jdbc.OracleDriver
Driver Name:         Other

(Примечание: имя драйвера «Другое», а не «Oracle» — в этом случае будет использоваться драйвер Adobe Oracle, а не указанный драйвер Oracle.)

Затем вы можете вызвать хранимую процедуру, перейдя к необработанной Java, а не используя <cfstoredproc> или new StoredProc().

<cfscript>
array       = JavaCast( "string[]", [ 'A', 'B', 'C' ] );
try {
  connection  = createObject( 'java', 'coldfusion.server.ServiceFactory' )
                  .getDataSourceService()
                  .getDataSource( 'orcl' )
                  .getConnection()
                  .getPhysicalConnection();
  description = createObject( 'java', 'oracle.sql.ArrayDescriptor' )
                  .createDescriptor( 'STRINGLIST', connection );
  oracleArray = createObject( 'java', 'oracle.sql.ARRAY' )
                  .init( description, connection, array );

  statement   = connection.prepareCall( '{call test_proc( :input, :output )}' );
  statement.setARRAYAtName( "input", oracleArray );
  stringType  = createObject( 'java', 'java.sql.Types' ).VARCHAR;
  statement.registerOutParameter( "output", stringType );
  statement.executeQuery();

  returnValue = statement.getString( "output" );
}
finally
{
  if ( isDefined( "statement" ) )
    statement.close();
  if ( isDefined( "connection" ) )
    connection.close();
}
</cfscript>

Кроме того, вы также можете передать массив в запрос (и затем получить результат, который вы можете использовать в <cfloop>) следующим образом:

try {
  // set-up connection, etc. as above
  statement   = connection.prepareStatement( 'SELECT * FROM TABLE( :input )' );
  statement.setARRAYAtName( "input", oracleArray );
  resultSet   = statement.executeQuery();
  queryResult = createObject( 'java', 'coldfusion.sql.QueryTable' )
                .init( resultSet )
                .FirstTable();
}
finally
{
  if ( isDefined( "resultSet" ) )
    resultSet.close();
  if ( isDefined( "statement" ) )
    statement.close();
  if ( isDefined( "connection" ) )
    connection.close();
}
12.05.2016
  • Согласно вопросу, источник данных и хранимая процедура уже существуют. 12.05.2016
  • @DanBracuk Да, источник данных уже существует, но он не будет работать, если источник данных использует драйвер Adobe Oracle, который поставляется с ColdFusion, — дело в том, что вы должны использовать драйвер Oracle Oracle. Что касается вашего второго пункта, я никогда не переопределяю процедуру в ответе. 12.05.2016
  • @MT0 - я понимаю, что это всего лишь пример, но не забудьте закрыть все объекты инструкций, чтобы обеспечить правильное освобождение ресурсов. Как правило, соединение также закрывается (после всех операторов). Однако CF DSN обычно используют пул соединений, и я не помню, необходимо ли это при использовании ServiceFactory, поскольку это что-то вроде черного ящика. Я бы подумал, что да, но вы можете проверить это. 12.05.2016
  • @Leigh Согласен - глядя на этот вопрос, кажется, что закрытие соединения в пуле вернет его в пул для повторного использования, поэтому надо бы сделать. Я обновил код блоком try/finally, чтобы обрабатывать закрытие всего (блок catch не нужен, если только вы не хотите обрабатывать исключение встроенно, а не посредством обычной обработки исключений CF). 13.05.2016
  • Намного больше копаться во внутренностях — драйвер Oracle — это тонкий драйвер JDBC, тогда как драйвер Adobe/Macromedia, поставляемый с ColdFusion, — это драйвер DataDirect. 05.06.2017
  • Насколько я могу судить, вы можете поместить что угодно в поле «Имя драйвера» в CF Admin — это не обязательно «Другое» (вы уже указали другое) на предыдущем экране. 26.03.2018
  • Когда я попытался запустить приведенный выше код (с JDBC-драйвером тонкого клиента Oracle), я получил следующую ошибку: Missing IN or OUT parameter at index:: 2. Это произошло в statement.executeQuery();. Я не вижу никаких очевидных ошибок, поэтому я не совсем уверен, почему это происходит. 26.03.2018

  • 2

    Я играл с этим и обнаружил, что после того, как массив Oracle будет правильно создан (что включает в себя создание соединения, как указано выше, и создание массива Oracle соответствующего типа (в данном случае STRINGLIST) из исходного массива, тогда один может использовать <cfstoredproc> (и, я полагаю, <cfquery>) с параметром типа CF_SQL_ARRAY для выполнения хранимой процедуры (или запроса):

    <cfset the_datasource = "oratest" />
    <cfset the_array = javaCast("string[]", ["A","B","C"]) />
    <cfset return_value = "" />
    <cftry>
        <cfset the_connection = createObject("java", "coldfusion.server.ServiceFactory")
            .getDataSourceService()
            .getDataSource("#the_datasource#")
            .getConnection()
            .getPhysicalConnection()
        />
        <!---
        <cfset type_desc = createObject("java", "oracle.sql.ArrayDescriptor").createDescriptor("STRINGLIST", the_connection) />
        <cfset oracle_array = createObject("java", "oracle.sql.ARRAY").init(type_desc, the_connection, the_array) />
        --->
        <!--- oracle.SQL.ARRAY is deprecated; use this instead: --->
        <cfset oracle_array = the_connection.createOracleArray("STRINGLIST", the_array) />
        <cfset the_connection.close() />
    
        <cfstoredproc procedure="test_proc" datasource="#the_datasource#">
            <cfprocparam cfsqltype="CF_SQL_ARRAY" type="in" value="#oracle_array#" />
            <cfprocparam cfsqltype="CF_SQL_VARCHAR" type="out" variable="return_value" />
        </cfstoredproc>
    
    <cfcatch>
        <cfdump var="#cfcatch#" />
    </cfcatch>
    </cftry>
    
    <cfdump var="#return_value#" />
    

    Обратите внимание, что в приведенном выше коде я открываю соединение с базой данных только для создания массива Oracle. Я еще не понял, могу ли я попытаться использовать существующее соединение или повторно использовать соединение в вызове <cfstoredproc>.

    Надеюсь это поможет.

    ИЗМЕНИТЬ:

    Чтобы передать массив в запрос, вы можете просто сделать что-то вроде следующего:

    <cfquery name="get_table" datasource="#the_datasource#">
        SELECT * FROM TABLE( <cfqueryparam cfsqltype="CF_SQL_ARRAY" value="#oracle_array#" /> )
    </cfquery>
    
    26.03.2018
  • Интересно. Мне также интересно, действительно ли массивы относятся к соединению, или вам просто нужно какое-то соединение, чтобы получить материал метаданных. Жаль, что интерфейс настолько сложен, или вы могли бы создать массив с кодом CF :) (Кроме того, вы видели что-то в комментариях о закрытии/возврате соединений с пулом, поэтому, возможно, захотите добавить это). 26.03.2018
  • Я уверен, что вам нужно какое-то подключение к тому же SID Oracle, чтобы у вас был правильный тип массива. Но я предполагаю, что это не обязательно должно быть в том же DSN. 26.03.2018
  • Да, мне просто интересно, можете ли вы сделать это, скажем, один раз, когда приложение запускается, или вам нужно повторно открывать соединение каждый раз, когда вы запускаете процедуру. 26.03.2018
  • Похоже, это нужно будет сделать один раз для каждого уникального массива. Я не вижу способа обойти это. Кроме того, рассматривая проблему пула соединений - кажется, что используемые методы могут автоматически использовать соединение из пула, если оно существует? Пул соединений находится на уровне Java, а не на уровне CF. 26.03.2018
  • Да, из того, что я читал, будет использоваться объединенное соединение, если оно включено, но в комментариях упоминается, что вы все равно должны его close(). Если пул включен, это возвращает соединение с пулом. В противном случае он физически закрывает соединение. 26.03.2018
  • Спасибо за это - я заставил его работать на CF11, но не на (очень старом) CF9, поэтому похоже, что я застрял с переходом на Java (поэтому он работает на обеих версиях), пока оба сервера не будут обновлены до последней версии. . 27.03.2018
  • Интересно - CF_SQL_ARRAY и CF_SQL_STRUCT существуют уже некоторое время (хотя они не очень хорошо задокументированы), поэтому странно, что вышеизложенное не работает на CF9. Но думаю не удивительно! 27.03.2018
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..