Spring SimpleJdbcCall默认(可选)参数
我试图调用一个存储过程有默认(可选)参数而不传递它,它不工作。 基本上与此处所述的问题相同。
我的代码:
SqlParameterSource in = new MapSqlParameterSource()
.addValue("ownname", "USER")
.addValue("tabname", cachedTableName)
.addValue("estimate_percent", 20)
.addValue("method_opt", "FOR ALL COLUMNS SIZE 1")
.addValue("degree", 0)
.addValue("granularity", "AUTO")
.addValue("cascade", Boolean.TRUE)
.addValue("no_invalidate", Boolean.FALSE)
.addValue("force", Boolean.FALSE);
我得到一个例外:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Required input parameter 'PARTNAME' is missing
at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209)
根据这个,PARTNAME是一个可选参数。 也可以通过手动运行此程序而不使用PARTNAME参数来确认。
Ater放弃了这个问题,只是传递了所有的参数,包括可选的参数,因为boolean不是SQL数据类型,而只是PL / SQL。
所以我现在的解决方案是JDBC不适合运行存储过程,这就是我如何解决它的:
jdbcTemplate.execute(
new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException{
CallableStatement cs = con.prepareCall("{call sys.dbms_stats.gather_table_stats(ownname=>user, tabname=>'" + cachedMetadataTableName + "', estimate_percent=>20, method_opt=>'FOR ALL COLUMNS SIZE 1', degree=>0, granularity=>'AUTO', cascade=>TRUE, no_invalidate=>FALSE, force=>FALSE) }");
return cs;
}
},
new CallableStatementCallback() {
public Object doInCallableStatement(CallableStatement cs) throws SQLException{
cs.execute();
return null; // Whatever is returned here is returned from the jdbcTemplate.execute method
}
}
);
这是我采取的另一种方法。 我增加了用户设置他们将在通话中提供的参数数量的功能。 这将是头n个位置参数。 stored-proc中任何可用的参数都必须通过数据库的默认值处理进行设置。 这允许使用默认值将新参数添加到列表的末尾,或者可以无效,而不会破坏不知道提供值的代码。
我分类SimpleJdbcCall并添加了设置“maxParamCount”的方法。 我还用了一个邪恶的反射来设置我的CallMetaDataContext的子分类版本。
public class MySimpleJdbcCall extends SimpleJdbcCall
{
private final MyCallMetaDataContext callMetaDataContext = new MyCallMetaDataContext();
public MySimpleJdbcCall(DataSource dataSource)
{
this(new JdbcTemplate(dataSource));
}
public MySimpleJdbcCall(JdbcTemplate jdbcTemplate)
{
super(jdbcTemplate);
try
{
// Access private field
Field callMetaDataContextField = AbstractJdbcCall.class.getDeclaredField("callMetaDataContext");
callMetaDataContextField.setAccessible(true);
// Make it non-final
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(callMetaDataContextField, callMetaDataContextField.getModifiers() & ~Modifier.FINAL);
// Set field
callMetaDataContextField.set(this, this.callMetaDataContext);
}
catch (NoSuchFieldException | IllegalAccessException ex)
{
throw new RuntimeException("Exception thrown overriding AbstractJdbcCall.callMetaDataContext field", ex);
}
}
public MySimpleJdbcCall withMaxParamCount(int maxInParamCount)
{
setMaxParamCount(maxInParamCount);
return this;
}
public int getMaxParamCount()
{
return this.callMetaDataContext.getMaxParamCount();
}
public void setMaxParamCount(int maxInParamCount)
{
this.callMetaDataContext.setMaxParamCount(maxInParamCount);
}
}
在我的CallMetaDataContext子类中,我存储了maxInParamCount,并使用它来修剪已知存在于stored-proc中的参数列表。
public class MyCallMetaDataContext extends CallMetaDataContext
{
private int maxParamCount = Integer.MAX_VALUE;
public int getMaxParamCount()
{
return maxParamCount;
}
public void setMaxParamCount(int maxInParamCount)
{
this.maxParamCount = maxInParamCount;
}
@Override
protected List<SqlParameter> reconcileParameters(List<SqlParameter> parameters)
{
List<SqlParameter> limittedParams = new ArrayList<>();
int paramCount = 0;
for(SqlParameter param : super.reconcileParameters(parameters))
{
if (!param.isResultsParameter())
{
paramCount++;
if (paramCount > this.maxParamCount)
continue;
}
limittedParams.add(param);
}
return limittedParams;
}
}
除了最大参数计数之外,使用基本相同。
SimpleJdbcCall call = new MySimpleJdbcCall(jdbcTemplate)
.withMaxParamCount(3)
.withProcedureName("MayProc");
小小伙计:Spring对于IOC容器非常有用,这很有趣。 但是,在其实用类中,我不得不采取反思来提供一个依赖类的替代实现。
也在努力解决问题,并不想处理字符串。 如果我们从元数据中获得默认值,那么Spring可能并不关心默认实现,但可能会有更有趣的解决方案,但我只是将空值放在那里。 解决方案如下所示:
重写simpleJdbcCall
private class JdbcCallWithDefaultArgs extends SimpleJdbcCall {
CallableStatementCreatorFactory callableStatementFactory;
public JdbcCallWithDefaultArgs(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
protected CallableStatementCreatorFactory getCallableStatementFactory() {
return callableStatementFactory;
}
@Override
protected void onCompileInternal() {
callableStatementFactory =
new CallableStatementCreatorWithDefaultArgsFactory(getCallString(), this.getCallParameters());
callableStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());
}
@Override
public Map<String, Object> execute(SqlParameterSource parameterSource) {
((CallableStatementCreatorWithDefaultArgsFactory)callableStatementFactory).cleanupParameters(parameterSource);
return super.doExecute(parameterSource);
}
}
并覆盖CallableStatementCreatorFactory
public class CallableStatementCreatorWithDefaultArgsFactory extends CallableStatementCreatorFactory {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<SqlParameter> declaredParameters;
public CallableStatementCreatorWithDefaultArgsFactory(String callString, List<SqlParameter> declaredParameters) {
super(callString, declaredParameters);
this.declaredParameters = declaredParameters;
}
protected void cleanupParameters(SqlParameterSource sqlParameterSource) {
MapSqlParameterSource mapSqlParameterSource = (MapSqlParameterSource) sqlParameterSource;
Iterator<SqlParameter> declaredParameterIterator = declaredParameters.iterator();
Set<String> parameterNameSet = mapSqlParameterSource.getValues().keySet();
while (declaredParameterIterator.hasNext()) {
SqlParameter parameter = declaredParameterIterator.next();
if (!(parameter instanceof SqlOutParameter) &&
(!mapContainsParameterIgnoreCase(parameter.getName(), parameterNameSet))) {
logger.warn("Missing value parameter "+parameter.getName() + " will be replaced by null!");
mapSqlParameterSource.addValue(parameter.getName(), null);
}
}
}
private boolean mapContainsParameterIgnoreCase(String parameterName, Set<String> parameterNameSet) {
String lowerParameterName = parameterName.toLowerCase();
for (String parameter : parameterNameSet) {
if (parameter.toLowerCase().equals(lowerParameterName)) {
return true;
}
}
return false;
}
@Override
public void addParameter(SqlParameter param) {
this.declaredParameters.add(param);
}
链接地址: http://www.djcxy.com/p/65485.html