我正在从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
答案 0 :(得分:2)
由于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)本机('unwrapped')oracle连接(而不是代理)和b)传入的values数组。
execute
方法中,您需要通过Connection
获取原生JDBCTemplate
对象,并且确保每次都关闭代理连接。我们发现虽然通常 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数组类型 - 但这种技术对我们来说效果很好。
答案 1 :(得分:0)
除了您正在获得它之外
java.sql.SQLException:无法转换为内部表示:
数据类型不匹配。您可能正在尝试将String值分配给数据库中的字段,该字段可能不是字符串,也不是整数或其他内容。
我的建议是在您的数据库中执行查询,其次查看字段定义并检查您是否尝试插入不同数据类型的值。让我知道它是如何欢呼!!