Grails: simulate a sleeping proxy for testing

When using grails with a domain class hierarchy similar to the following:

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

and the service as follows:

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

We often encounter errors in the following:

No method signature: VehicleService.startRepairing () is applicable for Argument types: (Car _ $$ _ javassist_156) values: [Id: 42343, Class: Car]. Possible solutions: startRepairing (Car)

We believe this is happening because we are extracting the Vehicle instance from the collection, such as static hasMany = [vehicles: Vehicle] , which forces the proxy to implement the abstract Vehicle class, but not the concrete class ( Car , Motorcycle , etc.) .

We used to remove the argument type from the method as a solution, but we would prefer it - the code is cleaner, method overloading is possible, more friendly IDE ...

One solution that we were thinking about is to use the infamous GrailsHibernateUtil.unwrapIfProxy when the type does not match any other method:

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

But then the question arises: how can we verify this? When we run the code in development, we very rarely find the javassist problem, and even in production this happens โ€œby accidentโ€ (or, more precisely, because of conditions that avoid our knowledge :).

Is it possible to force an instance to be a javassist proxy? What would be a good strategy for such problems in general?

+6
source share
1 answer

Hibernate creates proxies when you need an instance of a class that loads lazily. You need something that is an instance or subclass of the expected class, and that after a full load, it basically acts like a loaded instance. Hibernate's approach to using a bytecode library to subclass your classes for use as a proxy works fine.

So, for 1-1 and for the "one" side 1 is a lot, the lazy instance will be a proxy. In addition, in lazy collections that are โ€œredundant,โ€ all instances will be proxies. This works better when you know that you only need data from the collection - to "fill" the collection, when it should be loaded on demand, the request searches only for identifiers, and instances in the collections will be proxies with only the identifier stored. If you loop the entire collection, you will run its N + 1 requests, but if you only need a few, it should generally be less resource-intensive than loading all the data for all instances when the collection is filled and members of the non-proxy collection are filled if only some of them are needed.

Another convenient place where you see the proxy is the load() method. get() looks through the 1st and 2nd (if active and enabled for the domain class) for previously loaded values โ€‹โ€‹and immediately goes to the database, returning null if there is no record for this identifier. This is no exception, as it is easy to find out if it was possible to do so. load() however gets into the database only when accessing a property other than id. If there is no record, an exception is thrown due to the fact that you are not always close (in time or in code) to the initial load() call that the proxy server created, and also because there is an implicit assumption that with lazy loading you "expecting a result, so the exception is exceptional in this case.

+6
source

Source: https://habr.com/ru/post/979393/


All Articles