2014-12-10 11 views
6

Quando si utilizza graal con una gerarchia di classi di dominio simile al seguente:Grails: simulare un proxy Hibernate per testare

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

e un servizio come la seguente:

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

Siamo molto frequentemente ci faccia errori come segue nella produzione:

Nessuna firma del metodo: VehicleService.startRepairing() è applicabile per tipi di argomenti: (Car _ $$ _ javassist_156) valori: [Id: 42343, Classe: Car]. Possibili soluzioni: startRepairing (Car)

Crediamo che questo accade perché recuperiamo l'istanza Vehicle da una collezione, come static hasMany = [vehicles: Vehicle], che fa sì che il proxy per implementare la classe astratta Vehicle ma non la classe concreta (Car, Motorcycle , eccetera).

Abbiamo usato per rimuovere il tipo di argomento dal metodo come una soluzione, ma ci sarebbe piuttosto avere - il codice è più pulito, overload del metodo è possibile, più IDE amichevole ...

Una soluzione abbiamo pensato è utilizzare il famigerato GrailsHibernateUtil.unwrapIfProxy quando il tipo non corrisponde a qualsiasi altro metodo:

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

Ma allora la domanda è: come possiamo provare questa? Quando si esegue il codice in fase di sviluppo, troviamo molto raramente il problema del javassist, e anche in produzione sembra che si verifichi "casualmente" (o più precisamente, a causa di condizioni che sfuggono alla nostra conoscenza :).

E 'possibile forzare un'istanza come proxy javassist? Quale sarebbe una buona strategia per questo tipo di problemi in generale?

+0

Yo ucan usa il lato dinamico di groovy e dichiara Object al posto di Car nel tuo codice. In fase di runtime, il metodo verrà trovato sull'istanza proxy e tutto andrà bene. – MatRt

+0

Cambiare la firma del mio metodo a causa di limiti di test non mi suona bene: -/ – Deigote

risposta

6

Hibernate crea proxy quando è necessaria un'istanza di classe caricata pigramente. Hai bisogno di qualcosa che sia un'istanza o sottoclasse della classe prevista, e che una volta caricato a pieno titolo, si comporta fondamentalmente come farebbe un'istanza caricata con impazienza. L'approccio di Hibernate all'utilizzo di una libreria bytecode per creare una sottoclasse delle tue classi da utilizzare come proxy funziona bene.

Quindi per un 1-1 e per il lato "uno" di un 1-many, l'istanza caricata con il pigro sarà un proxy. Anche nelle raccolte pigro-caricate che sono "extra-pigri", le istanze saranno tutte proxy. Funziona meglio quando sai che avrai bisogno solo di dati da parte di una raccolta - per "popolare" la raccolta quando deve essere caricata su richiesta, la query cerca solo ID e le istanze nelle raccolte saranno proxy con solo l'ID memorizzato. Se esegui il ciclo dell'intera raccolta, finisci per eseguire le query N + 1, ma se hai bisogno solo di poche, in generale dovrebbe essere meno dispendioso di risorse rispetto al caricamento di tutti i dati per tutte le istanze quando la raccolta è popolato e creando membri di raccolta non proxy se solo alcuni sono necessari.

Un altro posto facile dove si vedono i proxy è con il metodo load(). get() cerca nel 1 ° e 2 ° (se attivo e abilitato per la classe dominio) per valori precedentemente caricati e va immediatamente al database altrimenti, restituendo null se non c'è alcun record per tale id. Non lancia un'eccezione poiché è facile sapere se ci sono riusciti. load() tuttavia colpisce solo il database se si accede a una proprietà diversa da id.Se non ci sono record, viene generata un'eccezione sia perché non si è necessariamente vicini (in termini di tempo o di codice) alla chiamata iniziale load() che ha creato il proxy, e anche perché c'è un'ipotesi implicita che con il caricamento lento, si è aspettandomi un risultato, quindi un null è eccezionale in questo caso.

+0

Grazie per la spiegazione Burt. Questo era più o meno ciò che pensavamo. Il problema però è difficile da verificare, poiché è difficile da prevedere. Inoltre, potresti confermare che il motivo per cui ci ritroviamo con un'istanza la cui classe è astratta è perché è stata caricata da una raccolta il cui altro lato è una classe astratta? – Deigote

Problemi correlati