Consider the following sequence of statements:
VehicleEntity vehicleEntityProxy = entityManager.getReference(VehicleEntity.class, carID);
VehicleEntity vehicleEntityInitialized = entityManager.find(VehicleEntity.class, carID);
The vehicleEntityProxy
is a proxy, because you wanted it to be (obtained via getReference).
In the second statement, you want an initialized instance, so you are obtaining it with find:
Find by primary key. Search for an entity of the specified class and
primary key. If the entity instance is contained in the persistence
context, it is returned from there.
So, find
will check whether the instance is already in the persistence context, it will determine that it is because there is already a proxy created in the first statement, it will initialize the proxy (because it delegates to Hibernate Session.get which never returns an uninitialized instance) and will return the proxy.
The similar thing happens if instead of the first statement you have loaded some other entity which has a lazy to-one association to the VehicleEntity
with the carID
id. Then a proxy is created in place of the real entity instance, and that proxy will be returned (and initialized if not already) from the find
method when finding the entity for the same id.
All of this further implies that instanceof
is not your friend when working with Hibernate proxies (putting aside that it is not your friend in general when it comes to good OOP code).
As a workaround you could deproxy the object:
if (entity instanceof HibernateProxy) {
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
}
But this is tedious and error prone (and unnecessarily exposes Hibernate specific API). Also, you may need to do it for all of the associated objects as well since they can be proxies also.
Better approach is to use proven OOP constructs and patterns, like the Visitor pattern:
class Vehicle {
...
public void accept(VehicleVisitor visitor) {
}
}
class Car extends Vehicle {
...
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
class Bus extends Vehicle {
...
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
interface VehicleVisitor {
void visit(Car car);
void visit(Bus bus);
}
Now, instead of:
Vehicle vehicle = entityManager.find(Vehicle.class, vehicleId);
if (vehicle istanceof Car) {
Car car = (Car) vehicle;
// Do something with car
}
if (vehicle istanceof Bus) {
Bus bus = (Bus) vehicle;
// Do something with bus
}
you can do:
class SomeVehicleVisitor implements VehicleVisitor {
public void visit(Car car) {
// Do something with car
}
public void visit(Bus bus) {
// Do something with bus
}
}
SomeVehicleVisitor visitor = ...
Vehicle vehicle = entityManager.find(Vehicle.class, vehicleId);
vehicle.accept(visitor);