следующий доступный идентификатор записи - PullRequest
4 голосов
/ 28 марта 2011
@user = User.new 

@user.id возвращает ноль, но я должен знать это, прежде чем сохранить.Возможно ли это?

Ответы [ 6 ]

11 голосов
/ 25 января 2013

ДА вы можете!

У меня был тот же вопрос и я исследовал документы.Способность решить этот вопрос на самом деле очень связана с типом вашей базы данных.

Oracle и Postgresql действительно имеют полезные функции, чтобы легко решить эту проблему.Для MySQL (oracle) или SkySQL (с открытым исходным кодом) это кажется более сложным (но все же возможным).Я бы порекомендовал вам избегать использования этих (MySQL / SkySQL) баз данных, если вам нужны расширенные инструменты для работы с базами данных.

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

Может возникнуть ситуация, когда у вас нет другого выбора: например, когда две таблицы ссылаются на себя и по соображениям безопасности вы не разрешаете УДАЛИТЬили ОБНОВЛЕНИЕ этих таблиц.

В этом случае вы можете использовать базу данных (PostgreSQL, Oracle) nextval для генерации следующего идентификационного номера без фактическоговставка новой записи.

Используйте ее вместе с find_by_sql методом rails.

Чтобы сделать это, например, с postgreSQL и Rails, выберите одну из ваших моделей rails идобавить метод класса (не метод экземпляра!).Это возможно с помощью слова " self " в начале имени метода. self сообщает Ruby, что этот метод может использоваться только классом, а не переменными его экземпляра (объектами, созданными с помощью 'new').

Модель My Rails:

class MyToy < ActiveRecord::Base

...

  def self.my_next_id_sequence
    self.find_by_sql "SELECT nextval('my_toys_id_seq') AS my_next_id"
  end
end

Когда вы генерируете таблицу с миграцией Rails, по умолчанию Rails автоматически создает столбец с именем id и устанавливает его как таблицу первичного ключа .Чтобы гарантировать, что вы не получите никакой « повторяющейся ошибки первичного ключа », Rails автоматически создает sequence внутри базы данных и применяет ее к столбцу id .Для каждой новой записи (строки), которую вы вставляете в вашу таблицу, база данных сама рассчитает, каким будет следующий идентификатор вашей новой записи.

Rails присваивает этой последовательности имя автоматически с именем таблицы , добавляемой с"_id_seq" .Следующую функцию PostgreSQL необходимо применить к этой последовательности, как объяснено здесь .

Теперь о find_by_sql , как объяснено здесь , оно создаст массив , содержащий новые экземпляры объектов вашего класса.Каждый из этих объектов будет содержать все столбцы, которые генерирует оператор SQL.Эти столбцы будут появляться в каждом новом экземпляре объекта в форме атрибутов. Даже если эти атрибуты не существуют в модели вашего класса !

Как вы правильно поняли, наша функция nextval будет возвращать только одно значение.Поэтому find_by_sql создаст массив , содержащий единственный экземпляр объекта с одним атрибутом .Чтобы упростить чтение значения этого самого атрибута, мы назовем результирующий столбец SQL "my_next_id", поэтому наш атрибут будет иметь то же имя.

Вот и все.Мы можем использовать наш новый метод:

my_resulting_array = MyToy.my_next_id_sequence
my_toy_object = my_resulting_array[0]
my_next_id_value = my_toy_object.my_next_id

И использовать его для решения нашей ситуации мертвой блокировки:

my_dog = DogModel.create(:name => 'Dogy', :toy_id => my_next_id_value)

a_dog_toy = MyToy.new(:my_dog_id => my_dog.id)
a_dog_toy.id = my_next_id_value
a_dog_toy.save

Помните, что , если вы не используетеваш my_next_id_value этот идентификатор будет потерян навсегда.(Я имею в виду, что в будущем она не будет использоваться ни одной записью).

База данных не ждет, когда вы ее используете.Если где-то в какое-то время вашему приложению нужно вставить новую запись в ваш my_table_example (может быть, в то же время, когда мы играем с my_next_id_sequence), база данных всегда будет присваивать идентификационный номер этой новой записи сразу после той, которую вы создали с помощьюmy_next_id_sequence, учитывая, что ваше my_next_id_value зарезервировано.Это может привести к ситуациям, когда записи в вашем my_table_example не будут отсортированы по времени их создания.

10 голосов
/ 28 марта 2011

Нет, вы не можете получить идентификатор перед сохранением.Идентификационный номер поступает из базы данных, но база данных не будет назначать идентификатор, пока вы не позвоните save.Все это предполагает, что вы используете ActiveRecord, конечно.

3 голосов
/ 21 апреля 2011

У меня была похожая ситуация.Я вызвал последовательность, используя find_by_sql в моей модели, которая возвращает массив модели.Я получил идентификатор от первого объекта Arry.что-то вроде ниже.

Class User < ActiveRecord::Base
        set_primary_key 'user_id'
        alias user_id= id=
    def self.get_sequence_id
        self.find_by_sql "select TEST_USER_ID_SEQ.nextval as contact_id from dual"
    end
end

и для класса, на который вы ссылаетесь пользовательская модель,

@users = User.get_sequence_id
user = users[0]
1 голос
/ 24 декабря 2014

В Oracle вы можете получить текущее значение последовательности с помощью этого запроса:

SELECT last_number FROM user_sequences where sequence_name='your_sequence_name';

Итак, в вашем модельном классе вы можете поместить что-то вроде этого:

class MyModel < ActiveRecord::Base
  self.sequence_name = 'your_sequence_name'

  def self.my_next_id_sequence
    get_data = self.find_by_sql "SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'"
    get_data[0].last_number
  end

end

И, наконец, в контроллере вы можете получить это значение следующим образом:

my_sequence_number = MyModel.my_next_id_sequence

Таким образом, вам не нужно получать следующее значение с помощью NEXTVAL, и вы не потеряете свой идентификатор.

1 голос
/ 28 марта 2011

Обычно идентификатор заполняется из последовательности базы данных автоматически.

В рельсах вы можете использовать событие after_create, которое дает вам доступ к объекту сразу после того, как он был сохранен (и, следовательно, у него есть идентификатор). Это будет охватывать большинство случаев.

При использовании Oracle у меня был случай, когда я хотел создать идентификатор самостоятельно (а не использовать последовательность), и в этом посте я предоставляю подробности, как я это сделал. Короче код:

# a small patch as proposed by the author of OracleEnhancedAdapter: http://blog.rayapps.com/2008/05/13/activerecord-oracle-enhanced-adapter/#comment-240
# if a ActiveRecord model has a sequence with name "autogenerated", the id will not be filled in from any sequence
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
  alias_method :orig_next_sequence_value, :next_sequence_value

  def next_sequence_value(sequence_name)
    if sequence_name == 'autogenerated'
      # we assume id must have gotten a good value before insert!
      id
    else
      orig_next_sequence_value(sequence_name)
    end
  end
end

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

Итак, хотя это определенно не рекомендуется, и вы хотите быть абсолютно уверенными, почему вы не захотите использовать идентификатор, сгенерированный последовательностью, если это необходимо, это наиболее определенно возможно. Вот почему я люблю ruby ​​и Ruby on Rails! :)

0 голосов
/ 28 марта 2011

То, что вы можете сделать, это User.max (id). который вернет самый высокий ID в базе данных, вы можете добавить 1. Это ненадежно, хотя может удовлетворить ваши потребности.

...