将ColdFusion数组作为绑定变量传递给Oracle Collection

时间:2016-05-12 13:01:33

标签: oracle coldfusion

给定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]

2 个答案:

答案 0 :(得分:3)

首先,设置使用Oracle JDBC drivers的数据源。下载相应的JAR文件并将其放在coldfusion实例的lib目录中,然后通过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();
}

答案 1 :(得分: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>