Ruby on rails Ruby教程和;“独特性”;

Ruby on rails Ruby教程和;“独特性”;,ruby-on-rails,Ruby On Rails,我一直在关注这个教程,我觉得很好,但它提到了一些关于唯一性属性的东西,我没有得到。 这是迄今为止的mu用户文件: class User < ActiveRecord::Base #these attributes can be modified by the users attr_accessible :name, :email; #validation testing validates :name, presence: true, length: {

我一直在关注这个教程,我觉得很好,但它提到了一些关于唯一性属性的东西,我没有得到。 这是迄今为止的mu用户文件:

class User < ActiveRecord::Base

    #these attributes can be modified by the users
    attr_accessible :name, :email;

    #validation testing
    validates :name, presence: true, length: { maximum: 50 }
    #regular expression (there is an official one)
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
    #and add it..
    validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
        uniqueness:  { case_sensitive: false }

end
class用户
它说:

“使用验证:唯一性并不保证唯一性

Alice无意中点击了两次“提交”,很快连续发送了两个请求。 发生以下顺序:请求1在内存中创建通过验证的用户,请求2执行相同操作,请求1的用户保存,请求2的用户保存。 结果:两个用户记录具有完全相同的电子邮件地址,尽管存在唯一性验证。“


我尝试使用控制台(以及User.create方法)创建两个具有相同电子邮件地址的用户,唯一性似乎很好,因为只有第一个用户进入了sqlite3。那么,什么会导致错误或唯一性失败呢?

我相信本教程试图说明的一点是,唯一性验证是由应用程序层实施的,而不是由数据库实施的。如果您有一个可以同时处理多个请求的系统,那么:唯一性验证会受到竞争条件的影响

唯一性的工作方式是(1)查询数据库中的匹配记录。如果未找到匹配的记录,则验证通过,(2)该记录保存到数据库。在步骤(1)和(2)之间,从理论上讲,另一个请求突入并向数据库添加匹配记录是可能的,尽管可能性不大


这个问题也可以通过在数据库中强制唯一性来避免。如何做到这一点取决于您使用的数据库;Rails并不能帮助您这样做。

我相信本教程试图说明的一点是,唯一性验证是由应用程序层而不是数据库强制执行的。如果您有一个可以同时处理多个请求的系统,那么:唯一性验证会受到竞争条件的影响

唯一性的工作方式是(1)查询数据库中的匹配记录。如果未找到匹配的记录,则验证通过,(2)该记录保存到数据库。在步骤(1)和(2)之间,从理论上讲,另一个请求突入并向数据库添加匹配记录是可能的,尽管可能性不大


这个问题也可以通过在数据库中强制唯一性来避免。如何做到这一点取决于您使用的数据库;Rails不会帮助您这样做。

如果您有两个RoR实例与同一台SQL server对话,可能会出现以下情况:

process A reads, no email like 'foo@bar'
process B reads, no email like 'Foo@BAR'
process A writes new User with 'foo@bar'
process B writes new User with 'Foo@BAR'
Ruby一次不支持多个
线程
,因此需要两个单独的RoR进程


如果您使用SQL编写唯一性强制,或者使用
DataMapper
而不是
ActiveRecord

可以解决此问题如果您有两个RoR实例与同一SQL server对话,则可能会出现以下情况:

process A reads, no email like 'foo@bar'
process B reads, no email like 'Foo@BAR'
process A writes new User with 'foo@bar'
process B writes new User with 'Foo@BAR'
Ruby一次不支持多个
线程
,因此需要两个单独的RoR进程


如果使用SQL编写唯一性强制,或者使用
DataMapper
而不是
ActiveRecord
,当两个并行进程试图保存冲突对象时,约束失败,则可以解决此问题

    Process 1                   Process 2

1.  Create Object in Memory
2.                              Create Object in Memory
3.                              Check DB -> valid
4.  Check DB -> valid
现在,两个进程都认为已经验证了其唯一性约束,并继续保存对象

5.  save
6.                              save
现在的问题是,两个进程在保存其对象之前都要检查唯一性。因此,两个过程都可以看到在检查时没有冲突。冲突仅在步骤5之后才开始存在。现在的问题是,在步骤6中未对其进行检查,因此流程6会保存一个不遵循唯一约束的无效对象

为了缓解这种情况,您可以在数据库上创建一个唯一的索引。这将确保您不能在数据库中插入无效数据,因为约束是由数据库以原子方式检查的,因此竞争条件不再发生


现在你只能依靠那个唯一的索引了。但是,您会注意到,如果在那里发生错误,您将不会得到很好的错误消息。所以在实践中,你应该同时使用这两种方法。因此,在大多数情况下,您可以依赖rails良好集成的独特约束。对于少数情况下的竞争条件,您可以依靠唯一索引来确保数据100%保存。

当两个并行进程试图保存冲突对象时,约束将失败

    Process 1                   Process 2

1.  Create Object in Memory
2.                              Create Object in Memory
3.                              Check DB -> valid
4.  Check DB -> valid
现在,两个进程都认为已经验证了其唯一性约束,并继续保存对象

5.  save
6.                              save
现在的问题是,两个进程在保存其对象之前都要检查唯一性。因此,两个过程都可以看到在检查时没有冲突。冲突仅在步骤5之后才开始存在。现在的问题是,在步骤6中未对其进行检查,因此流程6会保存一个不遵循唯一约束的无效对象

为了缓解这种情况,您可以在数据库上创建一个唯一的索引。这将确保您不能在数据库中插入无效数据,因为约束是由数据库以原子方式检查的,因此竞争条件不再发生

现在你只能依靠那个唯一的索引了。但是,您会注意到,如果在那里发生错误,您将不会得到很好的错误消息。所以在实践中,你应该同时使用这两种方法。所以