2014-12-10 3 views
6

При использовании Grails с иерархией классов домена, подобной следующем:Grails: имитировать спящий режим прокси-сервер для тестирования

abstract class Vehicle { ... } 
class Car extends Vehicle { ... } 
class Motorcycle extends Vehicle { ... } 

и сервис как следующее:

class VehicleService { 
    def startRepairing(Car car) { ... } 
    def startRepairing(Motorcycle motorcycle) { ... } 
} 

Мы очень часто мы сталкиваемся ошибки в тексте:

Нет подписи метода: VehicleService.startRepairing() применимо для типы аргументов: (Car _ $$ _ javassist_156) значения: [Id: 42343, класс: Car]. Возможные решения: startRepairing (Car)

Мы считаем, что это происходит потому, что мы получаем экземпляр Vehicle из коллекции, такие как static hasMany = [vehicles: Vehicle], что приводит к прокси для реализации абстрактного класса Vehicle, но не конкретного класса (Car, Motorcycle , и т.д).

Мы использовали, чтобы удалить тип аргумента из метода в качестве решения, но мы предпочли бы иметь его - код чист, перегрузка метода возможна, более IDE дружеская ...

Одно решения, мы думали о том, использовать пресловутый GrailsHibernateUtil.unwrapIfProxy, когда тип не соответствует какому-либо другому методу:

class VehicleService { 
    def startRepairing(Vehicle vehicle) { 
     startRepairing(GrailsHibernateUtil.unwrapIfProxy(vehicle)) 
    } 
    def startRepairing(Car car) { 
     /* actual business logic here */ 
    } 
    def startRepairing(Motorcycle motorcycle) { 
     /* actual business logic here */ 
    } 
} 

Но тогда возникает вопрос, как мы можем проверить это? При запуске кода в разработке мы очень редко находим проблему javassist, и даже в производстве это происходит «случайно» (или, точнее, из-за условий, которые избегают наших знаний :).

Можно ли заставить экземпляр быть javassist proxy? Что было бы хорошей стратегией для подобных проблем в целом?

+0

Yo ucan использует динамическую сторону groovy и объявляет Object вместо Car в вашем коде. Во время выполнения метод будет найден на экземпляре прокси, и все будет хорошо. – MatRt

+0

Изменение подписи моего метода из-за ограничений тестирования не очень хорошо для меня: -/ – Deigote

ответ

6

Hibernate создает прокси-серверы, если вам нужен экземпляр класса, который лениво загружен. Вам нужно что-то, что является экземпляром или подклассом ожидаемого класса, и что после полной загрузки он в основном действует как загруженный экземпляр. Подход Hibernate к использованию библиотеки байт-кода для создания подкласса ваших классов для использования в качестве прокси-серверов работает хорошо.

Так что для 1-1 и для «одной» стороны 1-много, ленивый экземпляр будет прокси. Кроме того, в ленивых коллекциях, которые являются «лишними», все экземпляры будут прокси. Это работает лучше, когда вы знаете, что вам нужны только данные из коллекции - для «заполнения» коллекции, когда она должна быть загружена по запросу, запрос ищет только идентификаторы, а экземпляры в коллекциях будут прокси-серверами с сохраненным только идентификатором. Если вы зацикливаете всю коллекцию, вы закончите ее N + 1 запросов, но если вам нужно только несколько, то в общем случае она должна быть менее ресурсоемкой, чем загрузка всех данных для всех экземпляров, когда коллекция заполнять и создавать члены, не связанные с прокси-сервером, если нужны только некоторые из них.

Другое место, где вы видите прокси, - это метод load(). get() смотрит в 1-й и 2-й (если активен и включен для класса домена) для ранее загруженных значений и немедленно переходит в базу данных, возвращая значение null, если для этого идентификатора нет записи. Это не исключение, так как легко узнать, удалось ли ему это сделать. однако попадает только в базу данных, если вы получаете доступ к свойству, отличному от id.Если нет записи, исключение возникает как из-за того, что вы не обязательно близки (по времени или по коду) к начальному звонку load(), который создал прокси-сервер, а также потому, что существует неявное предположение, что при ленивой загрузке вы ожидая результата, поэтому в этом случае исключение является исключительным.

+0

Спасибо за объяснение Берт. Это было более или менее то, что мы думали. Проблема в том, что это трудно проверить, поскольку трудно предсказать. Кроме того, вы могли бы подтвердить, что причина, по которой мы заканчиваем экземпляр, чей класс является абстрактным, заключается в том, что он был загружен из коллекции, чья другая сторона является абстрактным классом? – Deigote