我在这里使用的是 Rails 2.3.14,但我遇到的问题可能并不局限于这个特定版本。这是 Rails 3 中仍然存在的所有关联和急切加载功能,并且在 2.3.14 之前就已经存在。我正在从 rails 2.3.8 升级,我没有遇到下面描述的问题。
下面的代码是基于更复杂的生产系统的模型。我概述的类/模块方案是这样设置的,这是有原因的。实际上,我在这个模型中包含了比演示问题所需的更多细节,希望使系统的整体结构更加清晰。
假设我有几个可以“驱动”的域对象,包括车辆(汽车/卡车)和高尔夫球。对于其中的每一件事,我都有一个 ActiveRecord 类:
class Vehicle < ActiveRecord::Base
end
class Car < Vehicle
include Driveable
end
class Truck < Vehicle
include Driveable
end
class GolfBall < ActiveRecord::Base
include Driveable
end
首先注意GolfBall
是一个顶层模型类,对应的数据库表是golf_balls
. 另一方面,Car
和Truck
是 的子类Vehicle
。车辆数据库是使用 STI 设置的,因此Cars
两者Trucks
都对应于vehicles
表(带有type
列微分器)。
其次,请注意,我Drivable
在所有底层域对象(Car
, Truck
, GolfBall
)上都包含了一个模块,它看起来像这样(在实际系统中,这个模块也做了很多事情,包括根据特定的设置进行设置,包括域对象):
module Driven
def self.included(base)
base.class_eval do
has_one :driver, :as => :driveable, :class_name => "#{self.name}Driver", :dependent => :destroy
end
end
end
所以这些东西中的每一个都可以有一个Driver
,并且它使用:class_name
基于包含类名称的 a(例如,在一个with中包含类Car
结果),因为这些引用的类中的每一个(等等......)都包含特定的业务逻辑,这是必要的协会的用途。has_one
:class_name => "CarDriver"
CarDriver
有一个建立多态关联的顶级Driver
类,然后是与上述域对象驱动程序类似的子类层次结构:
class Driver < ActiveRecord::Base
belongs_to :driveable, :polymorphic => true
end
class VehicleDriver < Driver
end
class CarDriver < VehicleDriver
end
class TruckDriver < VehicleDriver
end
class GolfBallDriver < Driver
end
这是基于单个数据库表drivers
,对所有子类使用 STI。
有了这个系统,我创建了一个新的Car
(存储在@car
下面)并将它与这样的新创建的相关联CarDriver
(在这个模型中它被分成这些特定的顺序步骤,以反映实际系统的工作方式):
@car = Car.create
CarDriver.create(:driveable => @car)
这在表中创建了数据库行,vehicles
如下所示:
id type ...
-----------------
1 Car ...
表格中的一行是drivers
这样的:
id driveable_id driveable_type type ...
--------------------------------------------------------
1 1 Vehicle CarDriver ...
Vehicle
是driveable_type
相反的,Car
因为车辆是 STI。到现在为止还挺好。现在我打开一个 Rails 控制台并执行一个简单的命令来获取一个Car
实例:
>> @car = Car.find(:last)
=> #<Car id: 1, type: "Car", ...>
根据日志,这是执行的查询:
Car Load (1.0ms)
SELECT * FROM `vehicles`
WHERE ( `vehicles`.`type` = 'Car' )
ORDER BY vehicles.id DESC
LIMIT 1
然后我得到CarDriver
:
>> @car.driver
=> #<CarDriver id: 1, driveable_id: 1, driveable_type: "Vehicle", type: "CarDriver", ...>
这导致该查询被执行。
CarDriver Load (0.7ms)
SELECT * FROM `drivers`
WHERE (`drivers`.driveable_id = 1 AND `drivers`.driveable_type = 'Vehicle') AND (`drivers`.`type` = 'CarDriver' )
LIMIT 1
但是,如果我尝试使用急切加载,我会得到不同的结果。从新的控制台会话中,我运行:
>> @car = Car.find(:last, :include => :driveable)
=> #<Car id: 1, type: "Car", ...>
>> @car.driver
=> nil
这导致nil
了司机。检查日志,第一条语句执行以下查询(常规查询和预加载查询):
Car Load (1.0ms)
SELECT * FROM `vehicles`
WHERE ( `vehicles`.`type` = 'Car' )
ORDER BY vehicles.id DESC
LIMIT 1
CarDriver Load (0.8ms)
SELECT * FROM `drivers`
WHERE (`drivers`.driveable_id = 1 AND `drivers`.driveable_type = 'Car') AND (`drivers`.`type` = 'CarDriver' )
LIMIT 1
如您所见,在急切加载的情况下,Car
查询与上述相同,但CarDriver
查询不同。它几乎是相同的,除了drivers.driveable
类型它正在寻找Car
它应该寻找 STI 基类名称的位置Vehicle
,就像它在非急切加载情况下所做的那样。
知道如何解决这个问题吗?