여기에서 레일즈 2.3.14를 사용하고 있습니다 만,이 특정 버전에만 국한되지는 않습니다. 이것은 레일즈 3에서 여전히 사용되고있는 2.3.14 이전의 모든 연관성과 열망하는 로딩 기능입니다. 레일즈 2.3.8에서 업그레이드하는 중입니다. 여기서 아래에 설명 된 문제가 없었습니다.Rails STI 모델 (다형성 배열 포함), 열거 형로드 쿼리가 잘못된 클래스 유형을 참조합니다.
아래의 코드는 훨씬 복잡한 생산 시스템을 기반으로 한 모형입니다. 내가 요약하고있는 클래스/모듈 구성표가 이유 때문에 이렇게 설정되었습니다. 사실상 문제를 설명하는 데 필요한 것보다 좀 더 자세한 내용을 포함하고 있습니다. 시스템의 전반적인 구조를 더 분명하게 만들 수 있습니다.
차량 (자동차/트럭) 및 골프 공을 포함하여 "구동"할 수있는 여러 도메인 개체가 있다고 가정합니다. GolfBall
최상위 모델 클래스와 해당 데이터베이스 테이블이 golf_balls
되는 것을
class Vehicle < ActiveRecord::Base
end
class Car < Vehicle
include Driveable
end
class Truck < Vehicle
include Driveable
end
class GolfBall < ActiveRecord::Base
include Driveable
end
첫째 주 : 이러한 것들의 각각에 대해, 나는 액티브 클래스가 있습니다. 한편, Car
및 Truck
은 서브 클래스가 Vehicle
입니다. 차량용 데이터베이스는 STI로 설정되므로 Cars
및 Trucks
은 모두 vehicles
테이블 (type
열 차별 자 포함)에 해당합니다.
, 나는 모든 최하위 레벨 도메인 객체 (Car
, Truck
, GolfBall
)에 Drivable
모듈을 포함하고있어주의, 그리고 실제 시스템에서 (이 같은 않는 많은도 포함이 모듈을 찾습니다 도메인 개체를 포함하여 특정에 따라 설정 일까지) :
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
을 가질 수 있으며, 예를 들면 has_one
와의 Car
결과 클래스를 포함합니다 (포함 클래스 이름을 기준으로 :class_name
를 사용하고 :class_name => "CarDriver"
) 참조 된 클래스 (CarDriver
, 등 ...)는 협회의 사용에 필요한 특정 비즈니스 로직을 포함합니다.
다음 최상위 Driver
다형성 연관을 설정 클래스 및 도메인 객체 드라이버 위와 유사한 서브 클래스 계층이있다 : 사용이 단일 데이터베이스 테이블 drivers
에 기초
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
은 모든 서브 클래스에 대한 STI.대신에이 시스템을
, 내가 만드는 새로운Car
(아래
@car
에 저장)과 같은
CarDriver
(이 방식을 반영하기 위해이 모형에서 이러한 특정 순차적 단계로 분할 새로 생성과 연결 실제 시스템 작동) :
@car = Car.create
CarDriver.create(:driveable => @car)
이 같은 vehicles
테이블이 생성 된 데이터베이스 행 :
id type ...
-----------------
1 Car ...
그리고 drivers
테이블의 행과 같은 이 : 차량 STI 때문에 Car
에 반대
id driveable_id driveable_type type ...
--------------------------------------------------------
1 1 Vehicle CarDriver ...
Vehicle
는 driveable_type
입니다. 여태까지는 그런대로 잘됐다. 지금은 레일 콘솔을 열고 Car
예를 얻을 수있는 간단한 명령을 실행
Car Load (1.0ms)
SELECT * FROM `vehicles`
WHERE (`vehicles`.`type` = 'Car')
ORDER BY vehicles.id DESC
LIMIT 1
그런 다음 나는 CarDriver
를 얻을 : 로그에 따르면
>> @car = Car.find(:last)
=> #<Car id: 1, type: "Car", ...>
을 여기에 실행 된 쿼리입니다 :
>> @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
그러나 열망하는로드를 사용하려고하면 다른 결과가 발생합니다. 새로운 콘솔 세션에서, 나는 실행이 드라이버에 대한 nil
결과
>> @car = Car.find(:last, :include => :driveable)
=> #<Car id: 1, type: "Car", ...>
>> @car.driver
=> 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
을 찾아야합니다.
이 문제를 해결하는 방법은 무엇입니까?
예, 이는 동일한 버그로 인한 것 같습니다. 내가 아는 한,이 버그는 결코 고쳐지지 않았고, 심지어는 공식적으로보고되지도 않았으므로, 여전히 3.2.8에있을 수 있습니다. 이 버그로 인해 정확하게 쿼리가 수행하는 sti 데이터가 미리로드되지 않고 속도 차이가 설명됩니다. Rails 2.3.14에서 sti 클래스의 "abstract_class = true"문제를 보지 못했고 문제없이 몇 달 동안 해당 솔루션을 구현 한 프로덕션 앱을 실행했습니다. Rails 3.2.8에서 변경된 사항이 있으면 말할 수 없습니다. –