2012-10-31 15 views
7

デフォルトの(オプションの)引数を渡すことなくストアドプロシージャを呼び出そうとしていて、動作していません。基本的には、hereと同じ問題です。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); 

そして、私は例外を取得:

partnameが thisに応じて任意のパラメータである
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Required input parameter 'PARTNAME' is missing 
    at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209) 

。また、PARTNAME引数を使用せずにこの手順を手動で実行できることも確認されています。

答えて

3

この質問をあきらめて、オプションのものも含めてすべてのパラメータを渡すだけで、booleanはSQLデータ型ではなくPL/SQLだけであるため、boolean引数を渡すことができませんでした。

だから私の現在のソリューションは、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 
      } 
     } 
); 
0

も問題に苦しんでいた、と対処したくありませんでした文字列で。 私たちがメタデータからデフォルト値を取得した場合、より興味深い解決法があるかもしれません。春はデフォルトの実装では気にしませんが、単にそこにnullを置くだけです。 ソリューションは、次のようになりました:

オーバーライド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); 
} 

1

ここで私が撮影してきた別のアプローチです。私は、ユーザーがコールで提供するパラメータの数を設定する機能を追加しました。これらは最初のn個の位置パラメータになります。 stored-procで使用可能な残りのパラメータは、データベースのデフォルト値処理を介して設定する必要があります。これにより、新しいパラメータをデフォルト値でリストの末尾に追加するか、値を知らないコードを壊すことなくヌル値にすることができます。

私はSimpleJdbcCallをサブクラス化し、 "maxParamCount"を設定するメソッドを追加しました。また、私のサブクラス化されたバージョンのCallMetaDataContextを設定するために少し悪い反射を使用しました。私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); 
    } 
} 

、私はmaxInParamCountを保存し、保存されたインプロセスに存在することが知られているパラメータのリストをトリミングするためにそれを使用。

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"); 

SMALL RANT:SpringがIOCコンテナとしてよく知っていることは面白いです。しかし、そのユーティリティクラスでは、依存クラスの代替実装を提供するためにリフレクションに頼らざるを得ません。

0

今日はまともな解決策ではなく、デフォルトではない値に対応し、フルーティーなリフレクション技術は使用しません。関数のメタデータ・コンテキストを外部的に作成して、すべてのパラメータ・タイプなどを取り出し、SimpleJdbcCallを手動で構築します。

まず、機能のためのCallMetaDataContextを作成します。

CallMetaDataContext context = new CallMetaDataContext(); 
    context.setFunction(true); 
    context.setSchemaName(schemaName); 
    context.setProcedureName(functionName); 
    context.initializeMetaData(jdbcTemplate.getDataSource()); 
    context.processParameters(Collections.emptyList()); 

次に、SimpleJdbcCallを作成するのではなく、独自のメタデータの検索をしないように強制:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate); 
// This forces the call object to skip metadata lookup, which is the part that forces all parameters 
simpleJdbcCall.setAccessCallParameterMetaData(false); 

// Now go back to our previously created context and pull the parameters we need from it 
simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(0)); 
for (int i = 0; i < params.length; ++i) { 
    simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(i)); 
} 
// Call the function and retrieve the result 
Map<String, Object> resultsMap = simpleJdbcCall 
         .withSchemaName(schemaName) 
         .withFunctionName(functionName) 
         .execute(params); 
Object returnValue = resultsMap.get(context.getScalarOutParameterName()); 
関連する問題