mysql 为什么activerecord不填充从创建返回的项中的自动递增列?

vfhzx4xs  于 2022-12-03  发布在  Mysql
关注(0)|答案(2)|浏览(124)

为什么rails没有在从create返回的项中填充一个自动递增的列?有没有更好的方法来做到这一点?
在Rails中,当执行a = Foo.create时,将填充a.id
但是如果你有一个通过

def up   
  execute "ALTER TABLE my_table ADD COLUMN my_auto_incrementing_column INTEGER AUTO_INCREMENT not null UNIQUE KEY;"
end

那么当你使用创建时,那个字段就不会出现。你还必须使用重新加载。

a = Foo.create
a.id # not nil
a.my_auto_incrementing_column # nil
a.reload
a.my_auto_incrementing_column # is now populated

版本信息:

$ ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-darwin14.5.0]
$ bundle exec rails -v
Rails 3.2.12
$ mysql --version
mysql  Ver 14.14 Distrib 5.6.26, for osx10.10 (x86_64) using  EditLine wrapper

一些背景:
这段代码被应用到一个现有的大型生产Rails代码库中,该代码库要求所有的id字段都是UUID。auto_increment列不是主键,因为它是在我们发现一个新的外部集成合作伙伴无法使用我们现有的长唯一标识符(UUID)处理之后添加的。
我们正在努力更新我们的ruby版本,但我们不想等待它作为这个问题的解决方案。而且,在阅读了activerecord中的changelogs之后,我仍然没有证据表明任何未来版本的ruby/rails将包含针对这个问题的bug修复。
我想改进的代码:

class Foo < ActiveRecord::Base
  has_one :object_containing_auto_incrementing_column

  def my_method
    if self.object_containing_auto_incrementing_column.nil?
      self.object_containing_auto_incrementing_column = ObjectContainingAutoIncrementingColumn.create(owner: self)
      self.object_containing_auto_incrementing_column.reload
    end
    self.object_containing_auto_incrementing_column.my_auto_incrementing_column
  end
end
hts6caw3

hts6caw31#

After looking at the source code it does not appear that ActiveRecord tries to populate auto-incrementing columns. It only assigns the value that is returned by the INSERT statement to the #id attribute and nothing else.

# ActiveRecord:: Persistence::ClassMethods
 def create
 # ...
   self.id ||= new_id if self.class.primary_key
 # ...
 end

If you want to populate my_auto_incrementing_column without hitting the DB twice, I think there is no way around patching ActiveRecord itself.
Have a look at how the insert method is implemented:

# Returns the last auto-generated ID from the affected table.
  #
  # +id_value+ will be returned unless the value is nil, in
  # which case the database will attempt to calculate the last inserted
  # id and return that value.
  #
  # If the next id was calculated in advance (as in Oracle), it should be
  # passed in as +id_value+.
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
    sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
    value      = exec_insert(sql, name, binds)
    id_value || last_inserted_id(value)
  end

There might not be any trivial way to change the current API to populate your field in question.

ql3eal8s

ql3eal8s2#

显然,仍然不可能在create()中获取SEQUENCE在数据库级别自动增量中生成的字段(不是id)。
我解决了这个问题
在我例子中:PostgreSQL按序列自动增量
使用after_create回调

  • 迁移 *
class CreateFoo < ActiveRecord::Migration[7.0]
  def change
    create_table :foo do |t|
      t.integer :autoinc_field
    end
    execute "CREATE SEQUENCE table_name_seq OWNED BY table_name.autoinc_field INCREMENT BY 1 START WITH 100000"
    execute "ALTER TABLE table_name ALTER COLUMN autoinc_field SET DEFAULT nextval('table_name_seq');"
  end
end
  • 型号 *
class Foo < ApplicationRecord
  after_create :reload
end
  • 结果 *
>> Foo.create!.autoinc_field 
=> 100000

相关问题