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

上一篇: Spring SimpleJdbcCall default (optional) arguments

下一篇: Using a background worker in ASP.NET with AJAX