2014-12-10 21 views
6

Podczas używania Grails z klasy domeny hierarchii podobny do następującego:Grails: symulować proxy hibernacji do testowania

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

i usługi jak:

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

My bardzo często mamy do czynienia błędy następujące w produkcji:

Brak podpisu metody: VehicleService.startRepairing() dotyczy typy argumentów: (Car _ $$ _ javassist_156) wartości: [Id: 42343, Class: Car]. Możliwe rozwiązania: startRepairing (CAR)

Wierzymy, że to się dzieje, ponieważ pobierzemy instancję Vehicle z kolekcji takich jak static hasMany = [vehicles: Vehicle], co powoduje, że serwer proxy do wdrożenia klasy abstrakcyjnej Vehicle ale nie klasy betonu (Car, Motorcycle itp.).

Użyliśmy usunąć typ argumentu ze sposobu w postaci roztworu, ale wolelibyśmy mieć go - kod jest czystsze, przeciążeniem metoda jest możliwe, bardziej przyjazne IDE ...

Jednym z rozwiązań jest myśleliśmy o używać niesławny GrailsHibernateUtil.unwrapIfProxy jeśli typ nie pasuje do żadnej innej metody:

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 */ 
    } 
} 

ale wtedy powstaje pytanie, w jaki sposób możemy to sprawdzić? Podczas pracy z kodem w rozwoju bardzo rzadko znajdujemy problem z javasistami, a nawet w produkcji wydaje się, że dzieje się to "losowo" (a dokładniej, z powodu warunków, które wymykają się naszej wiedzy).

Czy można wymusić, aby instancja była proxy pośredniczącym javassist? Jaka byłaby dobra strategia dla tego rodzaju problemów w ogóle?

+0

Używaj dynamicznej strony groovy i zadeklaruj Object zamiast Car w swoim kodzie. W czasie wykonywania metoda zostanie znaleziona na instancji proxy i wszystko będzie dobrze. – MatRt

+0

Zmiana sygnatury mojej metody z powodu ograniczeń testowania nie brzmi dla mnie dobrze: -/ – Deigote

Odpowiedz

6

Hibernate tworzy proxy, gdy potrzebujesz instancji klasy, która jest ładowana leniwie. Potrzebujesz czegoś, co jest instancją lub podklasą oczekiwanej klasy, i że po pełnym obciążeniu działa w zasadzie tak, jak gdyby była to bardzo obciążona instancja. Podejście Hibernate polegające na użyciu biblioteki kodu bajtowego do utworzenia podklasy twoich klas do wykorzystania jako proxy działa dobrze.

Tak więc dla 1-1 i dla "jednej" strony 1-wiele, instancja leniwego ładowania będzie serwerem proxy. Również w kolekcjach o luźnym załadowaniu, które są "wyjątkowo leniwy", wszystkie instancje będą pełnić rolę serwerów proxy. Działa to lepiej, gdy wiesz, że będziesz potrzebować tylko danych z niektórych kolekcji - do "zapełnienia" kolekcji, kiedy trzeba ją załadować na żądanie, zapytanie szuka tylko identyfikatorów, a wystąpienia w kolekcjach będą pełnomocnikami tylko z zapisanym identyfikatorem. Jeśli przejdziesz przez całą kolekcję, skończysz z jej zapytaniami N + 1, ale jeśli potrzebujesz tylko kilku, to generalnie powinno to wymagać mniejszego nakładu zasobów niż ładowanie wszystkich danych dla wszystkich instancji, gdy kolekcja jest wypełnione i tworzenie członków kolekcji bez pośrednictwa, jeśli tylko niektóre są potrzebne.

Innym łatwym miejscem, w którym widać proxy, jest metoda load(). get() wygląda na pierwszym i drugim miejscu (jeśli jest aktywny i włączony dla klasy domeny) dla wcześniej załadowanych wartości i przechodzi do bazy danych natychmiastowo, zwracając wartość null, jeśli nie ma rekordu dla tego identyfikatora. Nie rzuca wyjątku, ponieważ łatwo jest stwierdzić, czy się udało. load() jednak tylko trafi do bazy danych, jeśli uzyskasz dostęp do właściwości innej niż id.Jeśli nie ma rekordu, zgłoszony zostanie wyjątek, ponieważ niekoniecznie jest on bliski (czasowo lub pod kątem kodu) początkowe wywołanie load(), które utworzyło proxy, a także dlatego, że istnieje domniemane założenie, że przez leniwy załadunek jesteś oczekiwanie wyniku, więc wartość zerowa jest w tym przypadku wyjątkowa.

+0

Dzięki za wyjaśnienie Burt. To było mniej więcej to, co myśleliśmy. Problem jednak jest trudny do przetestowania, ponieważ trudno go przewidzieć. Czy mógłbyś również potwierdzić, że powodem, dla którego kończymy instancję, której klasa jest abstrakcyjna, jest to, że została załadowana z kolekcji, której druga strona jest klasą abstrakcyjną? – Deigote