[英]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數組作為參數傳遞給存儲過程。 這是怎么回事:
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數組。
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.