簡體   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