我的数据库中有帖子和组织。Posts belongs_to 组织和组织 has_many 帖子。
我的帖子表中有一个现有的 post_id 列,现在我在创建新帖子时手动增加该列。如何将自动增量添加到限定为 organization_id 的列?
目前我使用 mysql 作为我的数据库,但我计划切换到 PostgreSQL,所以如果可能的话,该解决方案应该适用于两者:)
非常感谢!
我的数据库中有帖子和组织。Posts belongs_to 组织和组织 has_many 帖子。
我的帖子表中有一个现有的 post_id 列,现在我在创建新帖子时手动增加该列。如何将自动增量添加到限定为 organization_id 的列?
目前我使用 mysql 作为我的数据库,但我计划切换到 PostgreSQL,所以如果可能的话,该解决方案应该适用于两者:)
非常感谢!
@richard-huxton 有正确的答案并且是线程安全的。
使用事务块并在该事务块内使用 SELECT FOR UPDATE。这是我的 Rails 实现。在 ruby 类上使用“事务”来启动事务块。在要锁定的行上使用“锁定”,基本上阻止所有其他并发访问该行,这是确保唯一序列号所需的。
class OrderFactory
def self.create_with_seq(order_attributes)
order_attributes.symbolize_keys!
raise "merchant_id required" unless order_attributes.has_key?(:merchant_id)
merchant_id = order_attributes[:merchant_id]
SequentialNumber.transaction do
seq = SequentialNumber.lock.where(merchant_id: merchant_id, type: 'SequentialNumberOrder').first
seq.number += 1
seq.save!
order_attributes[:sb_order_seq] = seq.number
Order.create(order_attributes)
end
end
end
我们为后台作业运行 sidekiq,因此我通过创建 1000 个后台作业来测试此方法,以使用 8 个工作人员(每个工作人员有 8 个线程)来创建订单。如果没有锁或事务块,重复的序列号会按预期发生。有了锁和事务块,所有的序列号看起来都是唯一的。
OK - I'll be blunt. I can't see the value in this. If you really want it though, this is what you'll have to do.
Firstly, create a table org_max_post (org_id, post_id)
. Populate it when you add a new organisation (I'd use a database trigger).
Then, when adding a new post you will need to:
BEGIN
a transactionSELECT FOR UPDATE
that organisation's row to lock itpost_id
by one, update the row.COMMIT
the transaction to complete your updates and release locks.You want all of this to happen within a single transaction of course, and with a lock on the relevant row in org_max_post. You want to make sure that a new post_id gets allocated to one and only one post and also that if the post fails to commit that you don't waste post_id's.
If you want to get clever and reduce the SQL in your application code you can do one of:
Deleting a post obviously doesn't affect your org_max_post
table, so won't break your numbering.
Prevent any updates to the posts at the database level with a trigger. Check for any changes in the OLD vs NEW post_id
and throw an exception if there is one.
Then delete your existing redundant id column in your posts table and use (org_id,post_id) as your primary key. If you're going to this trouble you might as well use it as your pkey.
Oh - and post_num
or post_index
is probably better than post_id
since it's not an identifier.
I've no idea how much of this will play nicely with rails I'm afraid - the last time I looked at it, the database handling was ridiculously primitive.
很高兴知道如何实现它。我更喜欢自己使用宝石。
首先,我必须说这不是一个好习惯,但我只会专注于解决您的问题:您始终可以通过在 PostsController 上执行以下操作来获取组织的帖子数:
def create
post = Post.new(...)
...
post.post_id = Organization.find(organization_id).posts.count + 1
post.save
...
end
您不应该自己更改数据库。让 ActiveRecord 处理它。