繁体   English   中英

春季存储过程通过数组

[英]Spring Stored Procedure Pass ARRAY

我正在从Spring调用存储过程,但是我需要将字符串数组传递给存储过程。 但是到目前为止,我遇到了一个接一个的错误。 有什么想法哪里出了问题吗?

我已经在Oracle中创建了一个自定义类型。

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

我定义了以下存储过程。 但是,目前,我只是迭代一个类型为TABLE OF的参数,以确保数据确实在做。 但是,现在我什至无法从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;

我将扩展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");
    }
}

我正在尝试运行以下测试。

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

这是我得到的堆栈跟踪。

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

由于Oracle中没有“内置”数组类型(至少直接与Java的数组类型相对应),因此还有更多的障碍可以成功地将Java数组作为参数传递给存储过程。 这是怎么回事:

  1. 定义一个可重用的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);
    }
}

这种类型的实例将需要访问a)本地(“未包装”)oracle连接(而不是代理),以及b)传入的values数组。

  1. execute方法中,您需要通过JDBCTemplate获取本机Connection对象,并至关重要地确保每次都关闭代理连接。 我们发现,虽然通常 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..
    }
}

这有点乏味,并且可能有更好的方法可以转换为自定义Oracle数组类型-但是此技术对我们来说效果很好。

从您得到的异常看来

java.sql.SQLException:无法转换为内部表示形式:

数据类型不匹配。 您可能正在尝试为数据库中的字段分配一个字符串值,而该字段可能不是字符串,而是整数或类似内容。

我的建议是在您的数据库中执行查询,其次查看字段定义并检查您是否要插入其他数据类型的值。 让我知道如何欢呼!!

暂无
暂无

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

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