Входные и исходящие параметры Sybase

#stored-procedures #jdbc #sqlanywhere #out-parameters #jconnect

#хранимые процедуры #jdbc #sqlanywhere #выходные параметры #jconnect

Вопрос:

Я схожу с ума от того, как драйвер Sybase JDBC обрабатывает хранимые процедуры со смешанными IN и OUT параметрами. Ознакомьтесь с этой простой хранимой процедурой:

 CREATE OR REPLACE PROCEDURE p (IN i1 INT, OUT o1 INT, IN i2 INT, OUT o2 INT)
BEGIN
    set o1 = i1;
    set o2 = i2;
END
  

И вот как я бы назвал это с помощью JDBC:

 CallableStatement c = connection.prepareCall("{ call dba.p(?, ?, ?, ?) }");
c.setInt(1, 1);
c.setInt(3, 2);
c.registerOutParameter(2, Types.INTEGER);
c.registerOutParameter(4, Types.INTEGER);
c.execute();
System.out.println(c.getObject(2));
System.out.println(c.getObject(4));
  

Но это приводит к

 1
null
  

Что происходит?? Это действительно серьезная ошибка в драйвере JDBC или я что-то полностью упускаю? Методом проб и ошибок я обнаружил, что это способ, которым это работает:

 c.setInt(1, 1);
c.setInt(2, 2);
c.registerOutParameter(3, Types.INTEGER);
c.registerOutParameter(4, Types.INTEGER);
c.execute();
System.out.println(c.getObject(3));
System.out.println(c.getObject(4));
  

Теперь результат

 1
2
  

Изменяет ли драйвер JDBC тайно порядок IN и OUT параметры??

Я использую SQL Anywhere 12 и jconn3.jar

Ответ №1:

Похоже на ошибку в драйвере.

Я подозреваю, что ошибочный драйвер ожидает, что параметры будут переданы / зарегистрированы в порядке (т. е. 1,2,3,4). Когда вы выполняете registerOut(2), оператор, по-видимому, забывает, что вы установили (3) 🙂

Или, может быть, все должно быть сделано после ввода. Опять же, это ошибка в драйвере.

Обновить

Подождите, вы не изменили процедуру для второго варианта? Этот результат не имеет никакого смысла. Если, как вы сказали, драйвер не выполняет переупорядочение. Что, мягко говоря, необычно.

ОБНОВЛЕНИЕ 2

Я декомпилировал драйвер. Он выполняет несколько довольно забавных игр с исходящими параметрами, и со всей этой суматохой я чувствую, что у них есть достаточный потенциал для ошибки там, но пока я ее явно не вижу.

Единственная забавная вещь, которую я заметил, это то, что, по-видимому, если параметр в позиции n не отсутствует, драйвер будет сканировать параметры вперед, пока не найдет значение; если значение не найдено, оно переходит к следующей строке:

   s.registerOutParameter(5,Types.INT);
  ...
  // no out value at position 4, it will go to 5 and get the value
  rs.getInteger(4);
  

ОБНОВЛЕНИЕ 3

Может быть интересно посмотреть выходные данные всех 4 параметров в примере 1, т.е.:

 CallableStatement c = connection.prepareCall("{ call dba.p(?, ?, ?, ?) }");
c.setInt(1, 1);
c.setInt(3, 2);
c.registerOutParameter(2, Types.INTEGER);
c.registerOutParameter(4, Types.INTEGER);
c.execute();
System.out.println(c.getObject(1));
System.out.println(c.getObject(2));
System.out.println(c.getObject(3));
System.out.println(c.getObject(4));
  

Комментарии:

1. ДА. Это одна и та же процедура, просто вызов отличается. Я также подозреваю, что этот драйвер выполняет переупорядочивание параметров. Но поскольку я не мог представить такой ужасный недостаток в драйвере крупной СУБД, я подумал, что, возможно, я что-то не так понял…

2. Я хотел декомпилировать драйвер, но не смог найти его с помощью Google, только новые версии. У вас есть ссылка, указывающая на этот глючный драйвер?

3. Вы можете найти это здесь: sybase.com/detail?id=1041013 . jconn3.jar содержится в пакете jconnect 6

4. Я предполагаю, что вы были недостаточно быстры для получения награды … Извините. Я ничего не знаю об этом бизнесе с вознаграждением. Теперь я поднял проблему и в meta: meta.stackexchange.com/questions/84785 /…

5. не заботьтесь о вознаграждении 😀 рассматриваемая проблема — это то, что является захватывающим!

Ответ №2:

Я попробовал это с Oracle 9.2, и это работает так, как ожидалось.. Я думаю, что эта проблема связана с вашим драйвером JDBC, а не с самим JDBC.

     public static void main(String[] args) throws Exception {

    Connection connection = getConnection();

    CallableStatement c = connection.prepareCall("{ call p(?, ?, ?, ?) }");
    c.setInt(1, 1);
    c.setInt(3, 2);
    c.registerOutParameter(2, Types.INTEGER);
    c.registerOutParameter(4, Types.INTEGER);
    c.execute();
    System.out.println(c.getObject(2));
    System.out.println(c.getObject(4));

}
  

Вывод:

 Connected to database
1
2
  

Комментарии:

1. Второй вариант, который вы упомянули в своем вопросе, приводит к нулю, null с моим драйвером Oracle 9.2 JDBC.

2. Вы написали в вопросе: «Это действительно серьезная ошибка в драйвере JDBC или я что-то полностью упускаю?» Вот почему я попробовал это с Oracle, чтобы убедиться, что это только в вашем драйвере JDBC. Основываясь на этом, я мог бы написать: да, Лукас, возможно, это ошибка в вашем драйвере sybase jdbc, который хорошо работает с Oracle. В любом случае: спасибо, что проголосовали против единственного, кто потратил время на изучение этой проблемы .. 🙁

3. Ах, смотрите. Вы автоматически получили половину вознаграждения … 😉

4. спасибо 🙂 забавно, как эти бесполезные очки репутации могут мотивировать скучающих людей

Ответ №3:

 Below sybase stored procedure works for me
public String IDGEN(String tableName,  Connection con , LOG _log)
    { 
        String strReturnValue = "-1"; 
        try
        {
             CallableStatement cs = con.prepareCall("{call usp_NEWPK_string_v6(?,?)}");
             cs.registerOutParameter(2,java.sql.Types.NUMERIC);
             cs.setString(1,tableName);
             cs.setInt(2,0);
             cs.execute();
             strReturnValue = cs.getLong(2)   "";

        } catch (Exception ex) {
            _log.logInstance(" ERROR: [IDGEN] "   ex.getMessage()); 
        }
        return strReturnValue;
    } 
// abobjects.com

create proc usp_NEWPK_string_v6 (@tablename  varchar(32) , @ID  numeric output
          )
as
declare @newValue     numeric
declare @oldValue  numeric
select @oldValue=IDGEN_ID from DB_IDGEN where IDGEN_TableName = @tablename
select @newValue=@oldValue   1 from DB
 _IDGEN where IDGEN_TableName = @tablename
update DB_IDGEN set IDGEN_ID = @newValue where IDGEN_TableName = @tablename
select @ID  = @oldValue
return