简体   繁体   English

春季存储过程通过数组

[英]Spring Stored Procedure Pass ARRAY

I'm working on calling a stored procedure from Spring but I need to pass an array of strings to the stored procedure. 我正在从Spring调用存储过程,但是我需要将字符串数组传递给存储过程。 However thus far I have encountered error after error. 但是到目前为止,我遇到了一个接一个的错误。 Any ideas where things are going wrong? 有什么想法哪里出了问题吗?

I've created a custom type in Oracle. 我已经在Oracle中创建了一个自定义类型。

create or replace type type_string_array 
AS TABLE OF VARCHAR(255);

I have the following stored procedure defined. 我定义了以下存储过程。 However for the moment I'm just having it iterate through one of the parameters that is an type TABLE OF to ensure the data is actually making it. 但是,目前,我只是迭代一个类型为TABLE OF的参数,以确保数据确实在做。 However I can't even call the procedure from Java at this point. 但是,现在我什至无法从Java调用该过程。

procedure sp_save_publication(
    p_id out t_tr_publication.id%type,
    p_tr_uid in t_tr_publication.tr_uid%type,
    p_title in t_tr_publication.title%type,
    p_item_title in t_tr_publication.item_title%type,
    p_cover_date in t_tr_publication.cover_date%type,
    p_issue in t_tr_publication.issue%type,
    p_sort_date in t_tr_publication.sort_date%type,
    p_volume in t_tr_publication.volume%type,
    p_page_begin in t_tr_publication.page_begin%type,
    p_page_end in t_tr_publication.page_end%type,
    p_accession_no in t_tr_publication.accession_no%type,
    p_issn in t_tr_publication.issn%type,
    p_doi in t_tr_publication.doi%type,
    p_doctypes in type_string_array,
    p_headings in type_string_array,
    p_keywords in type_string_array,
    p_organizations in type_string_array,
    p_publishers in type_string_array,
    p_sub_headings in type_string_array,
    p_sub_organizations in type_string_array,
    p_subjects in type_string_array
  ) AS
  BEGIN

    if p_doctypes.count > 0
    then

      for i in p_doctypes.FIRST .. p_doctypes.LAST
      loop

        pkg_logger.sp_log_error(
          p_code => null,
          p_message => p_doctypes(i),
          p_package_name => package_name,
          p_procedure_name => 'sp_save_publication'
        );

      end loop;

    end if;  

  END sp_save_publication;

I am extending Spring's StoredProcedure class as follows: 我将扩展Spring的StoredProcedure类,如下所示:

public class SPSavePublication extends StoredProcedure {

    public SPSavePublication(JdbcTemplate jdbcTemplate) {

        super(jdbcTemplate,"PKG_THOMSON_REUTER.sp_save_publication");
        declareParameter(new SqlOutParameter("p_id", Types.INTEGER));
        declareParameter(new SqlParameter("p_tr_uid",Types.VARCHAR));
        declareParameter(new SqlParameter("p_title",Types.VARCHAR));
        declareParameter(new SqlParameter("p_item_title",Types.VARCHAR));
        declareParameter(new SqlParameter("p_cover_date",Types.VARCHAR));
        declareParameter(new SqlParameter("p_issue", Types.VARCHAR));
        declareParameter(new SqlParameter("p_sort_date",Types.DATE));
        declareParameter(new SqlParameter("p_volume",Types.VARCHAR));
        declareParameter(new SqlParameter("p_page_begin",Types.VARCHAR));
        declareParameter(new SqlParameter("p_page_end",Types.VARCHAR));
        declareParameter(new SqlParameter("p_accession_no",Types.VARCHAR));
        declareParameter(new SqlParameter("p_issn",Types.VARCHAR));
        declareParameter(new SqlParameter("p_doi",Types.VARCHAR));
        declareParameter(new SqlParameter("p_doctypes",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_headings",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_keywords",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_organizations",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_publishers",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_sub_headings",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_sub_organizations",Types.ARRAY,"TYPE_STRING_ARRAY"));
        declareParameter(new SqlParameter("p_subjects",Types.ARRAY,"TYPE_STRING_ARRAY"));
        setFunction(false);
        compile();
    }

    public long execute(String uid, String title, String itemTitle, String coverDate,
                        String issue, Date sortDate, String volume, String pageBegin,
                        String pageEnd, String accessionNo, String issn, String doi,
                        String[] doctypes, String[] headings, String[] keywords,
                        String[] organizations, String[] publishers, String[] subHeadings,
                        String[] subOrganizations, String[] subjects) {

        Map results = super.execute(uid,title,itemTitle,coverDate,issue,sortDate,volume,
                pageBegin,pageEnd,accessionNo,issn,doi,doctypes,headings,keywords,organizations,
                publishers,subHeadings,subOrganizations,subjects);
        return (int) results.get("p_id");
    }
}

I have the following Test I am trying to run. 我正在尝试运行以下测试。

@Test
    public void testSPSavePublication() {

        String[] docTypesArray = {"journal","doctypes"};

        SPSavePublication spSavePublication = new SPSavePublication(jdbcTemplate);
        spSavePublication.execute(null,null,null,null,null,new Date(),null,null,null,null,
                null,null,docTypesArray,docTypesArray,docTypesArray,docTypesArray,docTypesArray,
                docTypesArray,docTypesArray,docTypesArray);
    }

This is the stacktrace I'm getting. 这是我得到的堆栈跟踪。

org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call PKG_THOMSON_REUTER.sp_save_publication(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}]; SQL state [99999]; error code [17059]; Fail to convert to internal representation: [Ljava.lang.String;@7b10472e; nested exception is java.sql.SQLException: Fail to convert to internal representation: [Ljava.lang.String;@7b10472e
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1137)
    at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1173)
    at org.springframework.jdbc.object.StoredProcedure.execute(StoredProcedure.java:123)
    at org.company.app.procedures.thomsonreuter.SPSavePublication.execute(SPSavePublication.java:53)
    at org.company.app.ThomsonReutersTests.testSPSavePublication(ThomsonReutersTests.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.sql.SQLException: Fail to convert to internal representation: [Ljava.lang.String;@7b10472e
    at oracle.sql.ARRAY.toARRAY(ARRAY.java:244)
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:10472)
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9966)
    at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:10590)
    at oracle.jdbc.driver.OracleCallableStatement.setObject(OracleCallableStatement.java:6119)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:249)
    at com.mchange.v2.c3p0.impl.NewProxyCallableStatement.setObject(NewProxyCallableStatement.java:4025)
    at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:432)
    at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:235)
    at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:150)
    at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:213)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1115)
    ... 36 more

As there is no 'built-in' array type in Oracle (that directly corresponds to Java's array type at least), there is a few more hoops to jump through to successfully pass Java arrays as parameters to stored procedures. 由于Oracle中没有“内置”数组类型(至少直接与Java的数组类型相对应),因此还有更多的障碍可以成功地将Java数组作为参数传递给存储过程。 Here's how it goes: 这是怎么回事:

  1. Define a reusable Spring AbstractSqlTypeValue that will represent your array-of-strings type: 定义一个可重用的Spring AbstractSqlTypeValue ,它将代表您的字符串数组类型:
class OracleArrayType extends AbstractSqlTypeValue {

    private final Connection oracleCon;
    private final Object[] values;

    public OracleArrayType(final Connection oracleCon, final Object[] values) {
        this.oracleCon = oracleCon;
        this.values = values;
    }

    @Override
    protected Object createTypeValue(final Connection con, final int sqlType, final String typeName)
            throws SQLException {
        return new ARRAY(ArrayDescriptor.createDescriptor(typeName, oracleCon), oracleCon, values);
    }
}

Instances of this type will need access to a) the native ('unwrapped') oracle connection (rather than the proxy) and b) the values array being passed in. 这种类型的实例将需要访问a)本地(“未包装”)oracle连接(而不是代理),以及b)传入的values数组。

  1. Within your execute method you need to obtain the native Connection object via JDBCTemplate , and crucially ensure that the proxy connection is closed each time. execute方法中,您需要通过JDBCTemplate获取本机Connection对象,并至关重要地确保每次都关闭代理连接。 We found that whilst normally Spring's JDBCTemplate would be responsible for ensuring each connection is properly closed and returned to the pool, when the connection is unwrapped this does not happen and its therefore necessary to do it yourself, in this case using a try-with-resources block, eg: 我们发现,虽然通常 Spring的JDBCTemplate负责确保正确关闭每个连接并将其返回到池中,但是当打开连接时, 不会发生这种情况 ,因此需要您自己进行操作,在这种情况下,请使用try-with-resources块,例如:
public int execute(String uid, String title, String itemTitle, String coverDate, String issue, Date sortDate,
        String volume, String pageBegin, String pageEnd, String accessionNo, String issn, String doi,
        String[] doctypes, String[] headings, String[] keywords, String[] organizations, String[] publishers,
        String[] subHeadings, String[] subOrganizations, String[] subjects) {

    // Ensure proxy connection is closed properly
    try (final Connection connectionWrapper = getJdbcTemplate().getDataSource().getConnection()) {

        // Obtain native connection
        final Connection oracleConnection =
                getJdbcTemplate().getNativeJdbcExtractor().getNativeConnection(connectionWrapper);

        AbstractSqlTypeValue docTypeArray = new OracleArrayType(oracleConnection, doctypes);
        AbstractSqlTypeValue headingsArray = new OracleArrayType(oracleConnection, headings);
        AbstractSqlTypeValue keywordsArray = new OracleArrayType(oracleConnection, keywords);
        AbstractSqlTypeValue organizationsArray = new OracleArrayType(oracleConnection, organizations);
        AbstractSqlTypeValue publishersArray = new OracleArrayType(oracleConnection, publishers);
        AbstractSqlTypeValue subHeadingsArray = new OracleArrayType(oracleConnection, subHeadings);
        AbstractSqlTypeValue subOrganizationsArray = new OracleArrayType(oracleConnection, subOrganizations);
        AbstractSqlTypeValue subjectsArray = new OracleArrayType(oracleConnection, subjects);

        Map results = super.execute(uid, title, itemTitle, coverDate, issue, sortDate, volume,
                pageBegin, pageEnd, accessionNo, issn, doi, docTypeArray, headingsArray, keywordsArray,
                organizationsArray, publishersArray, subHeadingsArray, subOrganizationsArray, subjectsArray);

        return (int) results.get("p_id");
    } catch (SQLException e) {
        throw new DataRetrievalFailureException(e.getMessage()); // or whatever..
    }
}

It's a bit tedious and there may be better ways to translate into the custom Oracle array type - but this technique worked well for us. 这有点乏味,并且可能有更好的方法可以转换为自定义Oracle数组类型-但是此技术对我们来说效果很好。

From the exception that your are getting it seems that 从您得到的异常看来

java.sql.SQLException: Fail to convert to internal representation: java.sql.SQLException:无法转换为内部表示形式:

there is a datatype mismatch . 数据类型不匹配。 you may be trying to assign a String value to a field in database which may be defined not a String and as a Integer or something. 您可能正在尝试为数据库中的字段分配一个字符串值,而该字段可能不是字符串,而是整数或类似内容。

my suggestion would be to execute the query in you data base, secondly see the field definition and check that you are trying to insert a value of different data type. 我的建议是在您的数据库中执行查询,其次查看字段定义并检查您是否要插入其他数据类型的值。 let me know how it goes cheers!! 让我知道如何欢呼!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM