Получить вставленный идентификатор из подготовки сиквела

У меня есть подготовленный оператор вставки в Sequel (с использованием Oracle).

prepared_statement = DB[:table_name].prepare(:insert, :name, :value=>:$value)

Когда я называю это, строка добавляется просто отлично.

prepared_statement.call :value=>'Some value'

У меня есть триггер и последовательность, поэтому идентификатор будет сгенерирован автоматически. Я хотел бы вернуть только что добавленную строку (или идентификатор), но не понимаю, как это сделать. Я не могу использовать insert, так как значение представляет собой CLOB и может превышать 4000 символов.


person Hemlock    schedule 23.08.2011    source источник


Ответы (3)


В JRuby с помощью адаптера JDBC вы можете переопределить вставку и передать в возвращаемом предложении. Сложность заключается в том, что вы не всегда знаете, что такое первичный ключ на этом уровне, поэтому вам, возможно, придется использовать ROWID или запросить все столбцы обратно.

В итоге вы получите что-то похожее на это:

module Sequel
  module JDBC
    class Database
      def execute_insert_with_returning(conn, sql, opts = {})        
        columns = opts[:key_columns] || ["ROWID"]
        q = "{ call #{sql} returning #{columns.join(',')} into #{columns.collect {|_| '?'}.join(',')} }"
        stmt = conn.prepare_call(q)
        raise "Unable to prepare call for insert" if stmt.nil?

        begin
          columns.each_with_index do |_, index|
            stmt.registerOutParameter(index+1, JavaSQL::Types::VARCHAR)
          end
          return nil if 0 == stmt.executeQuery

          values = (1..columns.count).inject({}) do |memo, index|
            key = columns[index-1].downcase.to_sym rescue nil
            memo[key] = stmt.get_string(index) unless key.nil?
            memo
          end
          values
        ensure
          stmt.close
        end
      end # #execute_insert_with_returning

      alias execute_without_specialized_insert execute
      def execute(sql, opts={}, &block)
        if opts[:type] == :insert
          synchronize(opts[:server]) do |conn|
            execute_insert_with_returning conn, sql, opts
          end
        else
          execute_without_specialized_insert sql, opts, &block
        end
      end # #execute
    end # Database
  end # JDBC
end # Sequel

Я сделал что-то вроде этого, и это работает очень хорошо. Я думаю, нам также пришлось переопределить Sequel::Model, чтобы он передал первичный ключ как opts[:key_columns], но я могу неправильно помнить.

Это немного несущий кладж, который выполняет свою работу. Было бы более элегантно специализировать его для адаптера JDBC Oracle и гарантировать, что весь код обработки ошибок присутствует в исходном операторе выполнения. Учитывая время, я хотел бы получить что-то лучше и вернуть его проекту Sequel.

person sdeming    schedule 16.09.2011

Способ получения заполненных значений последовательности — через предложение RETURNING оператора INSERT, как я обсуждаю в этом ответе на аналогичный вопрос относительно CodeIgniter.

Я не уверен, поддерживает ли базовая версия RoR такой синтаксис, но представляется возможным расширить ActiveRecord для его обработки. Подробнее.

person APC    schedule 23.08.2011
comment
Я не использую Rails или ActiveRecord. - person Hemlock; 24.08.2011

Адаптер Sequel Oracle не имеет встроенной поддержки подготовленных операторов, поэтому он возвращается к выдаче обычного запроса. Если вы можете использовать JRuby, адаптер jdbc имеет встроенную поддержку подготовленных операторов, поэтому он должен работать там. Если вы не можете использовать JRuby, вам придется поработать над добавлением встроенной поддержки подготовленных операторов в адаптер Oracle. У меня нет доступа к установке Oracle, поэтому я не могу протестировать какую-либо поддержку, но буду рад дать совет, если у вас возникнут проблемы.

person Jeremy Evans    schedule 24.08.2011
comment
Я использую JRuby - должен был быть помечен как таковой. Оператор работает нормально, я просто не могу понять, как вернуть сгенерированный индекс. - person Hemlock; 24.08.2011
comment
Ах, мой плохой. Проблема в том, что субадаптер оракула Sequel jruby не реализует last_insert_id, а общий адаптер оракула переопределяет только метод вставки, поэтому он не обрабатывает подготовленные операторы (которые используют вызов). Это не должно быть сложно реализовать, но может быть какой-то шаблонный код, чтобы попытаться подключиться к подготовленным операторам (которые расширяют наборы данных с помощью модуля и не имеют своих собственных классов). Вероятно, это может быть связано с рефакторингом поддержки подготовленных операторов по умолчанию, чтобы специально проверять метод для вызова в случае :insert, чтобы упростить это. - person Jeremy Evans; 26.08.2011
comment
Спасибо за Ваш ответ. На данный момент у меня есть работа вокруг. Я избавился от триггера и последовательности и использую UUID для столбца id. - person Hemlock; 26.08.2011