Реализация вызова хранимой процедуры с дополнительными параметрами в Spring 3.0

Я пытался найти способ выполнять сохраненные вызовы процессов из Spring 3.0 в Oracle 11.2, имея в виду следующие элементы:

  • Многие вызовы выполняются хранимыми процедурами, которые имеют множество необязательных параметров (параметры, которые имеют значения по умолчанию).
  • Существуют хранимые процедуры, которые могут содержать сочетание: IN, OUT и / или IN OUT.
  • Мне не нужно обрабатывать OUT.

Я хотел бы иметь возможность вызывать сохраненную процедуру с необходимыми параметрами (обязательными и / или необязательными). Другими словами, я не хочу передавать значение (даже null) необязательным параметрам на свой выбор (похоже, когда null передается программно [хотя и не в PL / SQL] в сопоставитель параметров, значения по умолчанию не используются). Я попытался реализовать эти вызовы как можно большим количеством возможных способов, но ничего не помогло:

create or replace
procedure update_stored_proc (
h1 in boolean default false,
h2 in number,
h3 in varchar2 default 'H3',
h4 in varchar2 default 'H4',
h5 in varchar2 default 'H5',
h6 in out number
);

Для update_stored_proc() есть только два обязательных параметра (h2 и h6) и четыре необязательных. Моя конечная цель - вызвать update_stored_proc(), передав h1, h2 и h6. Даже когда я вызываю сохраненную процедуру через SimpleJdbcCall со всеми установленными значениями, я получаю исключение:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(updatingDataSource)
                    .withProcedureName("update_stored_proc")
                    .withoutProcedureColumnMetaDataAccess();

simpleJdbcCall.declareParameters(new SqlParameter("h1", OracleTypes.BOOLEAN))
              .declareParameters(new SqlParameter("h2", OracleTypes.NUMBER))
              .declareParameters(new SqlParameter("h3", OracleTypes.VARCHAR))
              .declareParameters(new SqlParameter("h4", OracleTypes.VARCHAR))
              .declareParameters(new SqlParameter("h5", OracleTypes.VARCHAR))
              .declareParameters(new SqlInOutParameter("h6", OracleTypes.NUMBER));

MapSqlParameterSource in = new MapSqlParameterSource()
            .addValue("h1", false, OracleTypes.BOOLEAN)
            .addValue("h2", 123, OracleTypes.NUMBER)
            .addValue("h3", "h3", OracleTypes.VARCHAR)
            .addValue("h4", "h4", OracleTypes.VARCHAR)
            .addValue("h5", "h5", OracleTypes.VARCHAR)
            .addValue("h6", "h6", OracleTypes.NUMBER);

simpleJdbcCall.compile();
simpleJdbcCall.execute(in);

Исключение, которое я получаю, указывает на то, что тип столбца каким-то образом недействителен:

org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call update_stored_proc(?, ?, ?, ?, ?, ?)}]; SQL state [99999]; error code [17004]; Invalid column type; nested exception is java.sql.SQLException: Invalid column type

Я заменил OracleTypes на Types и даже вынул withoutProcedureColumnMetaDataAccess(), но ошибка не исчезла.


person Malvon    schedule 24.03.2014    source источник


Ответы (1)


Оказалось, что этот вопрос требует нескольких решений, чтобы приступить к работе. Прежде всего, реализация стандартов SQL в Oracle не содержит типа BOOLEAN, даже несмотря на то, что его собственный язык сценариев PL / SQL поддерживает его (еще одна причина, по которой администраторы баз данных не используют тип BOOLEAN в своих хранимых процедурах). Решение, которое я придумал, заключалось в использовании анонимного блока, объявлении локальной переменной и назначении ее параметру BOOLEAN:

DECLARE
  h1_false BOOLEAN := false;
  h6_ NUMBER;
BEGIN
  update_stored_proc(
  h1 => h1_false,
  h2 => :h2,
  h6 => h6_
  );
END;

Обратите внимание, что меня не волнует значение параметра h1 (и, если на то пошло, параметр OUT / h6) для моей конкретной функциональности, но я мог представить, имея простой оператор IF THEN ELSE, можно назначить связанный параметр для h1, т. Е.

DECLARE
  h1_false BOOLEAN;
  h6_ NUMBER;
BEGIN
  IF :h1 THEN
    h1_false = true;
  ELSE
    h1_false = false;
  END IF;

  update_stored_proc(
  h1 => h1_false,
  h2 => :h2,
  h6 => h6_
  );
END;

Вторая проблема заключается в том, как Spring обрабатывает необязательные параметры. Если объявлять необязательные параметры, то SimpleJdbcCall Spring и StoredProcedure, если на то пошло, ожидают значения для этих параметров. Следовательно, нужно позаботиться об этих значениях, когда они станут доступными. В противном случае, если необязательный параметр не имеет значения, вы должны передать NULL, который не будет запускать значение DEFAULT параметра PL / SQL, которое будет использоваться. Это означает, что ваша строка запроса (в данном случае анонимный блок PL / SQL) должна быть сгенерирована динамически. Итак, анонимный блок становится:

DECLARE
  h1_false BOOLEAN := false;
  h6_ NUMBER;
BEGIN
  update_stored_proc(
  h1 => h1_false,
  h2 => :h2,
  h6 => h6_

Я перешел на StoredProcedure решение, а не на SimpleJdbcCall, как оказалось проще. Хотя я должен добавить, что мне пришлось расширить StoredProcedure, чтобы создать общий класс для моих классов хранимых процедур и использовать этот расширенный класс для моих настраиваемых классов хранимых процедур. В вашем StoredProcedure классе вы объявляете только необходимые параметры (убедитесь, что не compile() запрос на этом этапе):

declareParameter(new SqlParameter("h2", OracleTypes.NUMBER));

Примечание. Если вам не нужен параметр OUT, как в моем случае, не включайте его в свое объявление. В противном случае назначьте переменную привязки, то есть h6 => :h6, в анонимном блоке и объявите ее в своем StoredProcedure, то есть declareParameter(new SqlOutParameter("h6", OracleTypes.NUMBER));, и убедитесь, что вы получили значение :h6, когда execute() вернет Map<String, Object>. Если ваше значение OUT относится к типу BOOLEAN, то я не знаю, как получить это значение.

Остальные необязательные параметры должны динамически создаваться в вашем StoredProcedure. то есть:

if (someObj.getH3() != null) {
  declareParameter(new SqlParameter("h3", OracleTypes.VARCHAR));
  paramMap.put("h3", someObj.getH3());
  anonymousPLSQLBlockQueryString += " , h3 => :h3";
}

где paramMap представляет собой Map, который будет передан в StoredProcedure#execute(paramMap). Вы делаете то же самое с h4 и h5, и в конце вы должны убедиться, что правильно закрыли строку запроса анонимного блока, то есть:

anonymousPLSQLBlockQueryString += " ); END;";
setSql(anonymousPLSQLBlockQueryString);
setSqlReadyForUse(true);
compile();
person Malvon    schedule 06.04.2014