JPA EntityManager search method returns proxy

I have two objects. One is inherited from the other.

Example:

@Entity
@Table(name = "vehicle")
@Inheritance(strategy = InheritanceType.JOINED)
public class VehicleEntity {
    //id, etc., all reference fetch type is LAZY
}

@Entity
@Table(name = "car")
public class CarEntity extends VehicleEntity {
    //special parameters, all reference fetch type is LAZY
}

      

When I call find () on EntityManager with existing car id like this:

VehicleEntity vehicleEntity = entityManager.find(VehicleEntity.class, carID);

      

I am returning a proxy object, but I need to access the special methods of the CarEntity class because I would like to set some new parameters.

Can someone help me how can I do this?

Of course, this is just an example of a problem. More specifically: after calling find I check the instance of the returned object if it is "CarEntity", I set the parameters if I do nothing. I have many more inherited classes besides "CarEntity" and I am doing the same procedure as before. I know I can solve this problem with "find" s on specific object classes, but then I have to call "find" multiple times and when I search for any "VehicleEntity" I am always interested in the actual object, so the global one would be best decision.

+2


source to share


2 answers


Which I understood, since you are getting a proxy object that must be of type VehicleEntity, and the target in that proxy must be CarEntity.

So, in my opinion, you can deproxy the proxy object so that you can grab the real object, which will be CarEntity.

Then you can call the special CarEntity element.



I found a link that will deprox the proxy object in general (since you had a lot of inherited classes)

Converting Hibernate proxy to real object

Hope it helps.

+2


source


Consider the following sequence of statements:

VehicleEntity vehicleEntityProxy = entityManager.getReference(VehicleEntity.class, carID);
VehicleEntity vehicleEntityInitialized = entityManager.find(VehicleEntity.class, carID);

      

vehicleEntityProxy

is a proxy server because you wanted it to be (received through getReference

).

In the second statement, you need an initialized instance, so you get it with find

:

Find by primary key. Find the entity of the specified class and the primary key. If an instance of an object is contained in the persistence context, it is returned from there.

So, find

checks if an instance is already present in the persistence context, it will determine that it is because in the first assertion a proxy has already been created, it will initialize the proxy (because it delegates to Hibernate Session.get

, which never returns an uninitialized instance) and will return the proxy.

A similar situation occurs if, instead of the first statement, you loaded some other object that has an association lazy to-one with VehicleEntity

an identifier carID

. Then a proxy is created instead of a real instance of the object, and that proxy will be returned (and initialized if not already) from the method find

when looking up an object for the same ID.

All of this means that it is not your friend when working with Hibernate proxies (putting aside that it is not your friend at all when it comes to good OOP code). instanceof

As a workaround, you can deprox the object:



if (entity instanceof HibernateProxy) {
  entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
}

      

But this is tedious and error prone (and exposes a specific API to Hibernate unnecessarily). In addition, you may need to do this for all related objects as well, as they can also be proxies.

The best approach is to use proven OOP constructs and patterns like 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);

      

+6


source







All Articles