2012-10-31 11 views
7

मैं एक संग्रहीत प्रक्रिया का आह्वान करने की कोशिश कर रहा हूं जिसमें उन्हें पास किए बिना डिफ़ॉल्ट (वैकल्पिक) तर्क हैं और यह काम नहीं कर रहा है। अनिवार्य रूप से वही समस्या here वर्णित है।वसंत SimpleJdbcCall डिफ़ॉल्ट (वैकल्पिक) तर्क

Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Required input parameter 'PARTNAME' is missing 
    at org.springframework.jdbc.core.CallableStatementCreatorFactory$CallableStatementCreatorImpl.createCallableStatement(CallableStatementCreatorFactory.java:209) 

कहाँ PARTNAME this के अनुसार एक वैकल्पिक पैरामीटर है:

मेरे कोड:

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 तर्क w/o चला सकता हूं।

उत्तर

3

इस प्रश्न पर छोड़ने और वैकल्पिक पैरामीटर सहित सभी पैरामीटर को पार करने के लिए मैंने बूलियन तर्कों को पारित करने में असमर्थता में भाग लिया, क्योंकि बूलियन एसक्यूएल डेटा प्रकार नहीं है, केवल पीएल/एसक्यूएल है।

तो मेरे वर्तमान समाधान है कि 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

भी समस्या के साथ संघर्ष कर रहा था, और सौदा नहीं करना चाहता था तारों के साथ। अधिक दिलचस्प समाधान हो सकता है, अगर हमें मेटा डेटा से डिफ़ॉल्ट मान मिलते हैं, जो वसंत को डिफ़ॉल्ट कार्यान्वयन में परवाह नहीं करता है, लेकिन मैं बस वहां नल डालता हूं।

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

यहाँ

ओवरराइड simpleJdbcCall एक अलग दृष्टिकोण है कि मैं ले लिया है है: समाधान निम्नलिखित की तरह आया था। मैंने उपयोगकर्ता को कॉल पर प्रदान किए जा रहे पैरामीटर की संख्या निर्धारित करने की क्षमता को जोड़ा। ये स्थितित्मक मानकों की पहली संख्या होगी। संग्रहीत-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 की दुकान है, और इसका इस्तेमाल संग्रहीत-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"); 

लघु रेंट: यह मजाकिया है कि वसंत अपने आईओसी कंटेनर के लिए अच्छी तरह से जानता है। लेकिन, इसकी उपयोगिता कक्षाओं के भीतर, मुझे एक आश्रित वर्ग का वैकल्पिक कार्यान्वयन प्रदान करने के लिए प्रतिबिंब का सहारा लेना होगा।

0

आज के लिए एक सभ्य समाधान के साथ आया, जो गैर-शून्य डिफ़ॉल्ट के साथ copes, और फल प्रतिबिंब तकनीक का उपयोग नहीं करता है।यह सभी पैरामीटर प्रकारों को पुनः प्राप्त करने के लिए बाहरी रूप से फ़ंक्शन के लिए मेटाडेटा संदर्भ बनाकर काम करता है, फिर उस से सरलजेडबीसीकॉल मैन्युअल रूप से बना रहा है।

सबसे पहले, समारोह के लिए एक 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()); 
संबंधित मुद्दे