4

I have two entities. One 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 a find() on EntityManager with an existing car's id like this:

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

I get back 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 problem. More specifically: after I call find I'm checking the instance of the returned object, if it is "CarEntity" I set the parameters, if not I don't do anything. I have much more inherited classes beside "CarEntity", and I do the same procedure like before. I know I could solve this with "find"s on the specific object classes, but then I have to call "find" several times, and when I'm looking for any "VehicleEntity" I'm always interested in the actual object so the best would be a global solution.

Dragan Bozanovic
  • 21,631
  • 4
  • 36
  • 100
Csaba
  • 53
  • 1
  • 6
  • I think this is a Hibernate question (default JPA implementation used by WildFly), not a WildFly question. – Puce Sep 25 '15 at 12:15
  • It's unclear why you think you cannot use the specific type as the argument for the find method. Can you elaborate on this? – Puce Sep 25 '15 at 12:19
  • Thanks for the correction! This is my first ever question here, sorry for the mistakes. As I wrote I could use find method with specific types, but then I have to call find like 20-30 times (because of the lot of inherited classes). This is why I am looking for a global solution, to use find just once on the ancestor type, and determine the inherited type after. – Csaba Sep 25 '15 at 12:20
  • Are you sure you're getting a proxy back? Or some other instance of `VehicleEntity` or `VehicleEntity` itself (since it is not abstract)? – Dragan Bozanovic Sep 25 '15 at 12:47
  • I'm not sure it is a proxy, but I'm sure it is not the type of `CarEntity`, and that is the point. It says it is a `VehicleEntity` with some jvas...somenumbers. And when I check it with `instanceof CarEntity` it returns `false`. Actually the inheritance tree is much longer, and there are abstract classes before `VehicleEntity`, I just simplified the problem. – Csaba Sep 25 '15 at 13:05
  • 2
    Any reason you don't change your model to rely on polymorphism instead? Your model itself can determine which logic should be used, or such a method can return its class type even if proxied. – Chris Sep 25 '15 at 13:08
  • @Chris can you please give an example, or please explain your idea more deeply, because I do not understand you correctly. – Csaba Sep 25 '15 at 13:26
  • object.getClass() is likely what you are using. Write your own in each subclass that return the hard coded class directly - proxies would then return VehicleEntity.class, CarEntity.class or what ever you wanted. This might be more efficient than using instanceof, but you will run into maintanence issues if you ever subclass CarEntity or extend your model. Another way is to define a method on VehicleEntity that accepts a collection, and just implement it on CarEntity to do what ever is needed with the special parameters specific to the CarEntity. – Chris Sep 25 '15 at 16:59
  • Thanks @Chris, I get it now. This solution looks way better than using `instanceof`, but I think Dragan's answer is more applicable to this problem, so later I'm going with that. – Csaba Sep 28 '15 at 09:37
  • Use a JPA implementation that doesn't impose such handling on you ... OpenJPA, DataNucleus JPA certainly, EclipseLink also I think – Neil Stockton Jan 28 '16 at 14:14

2 Answers2

8

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);
Dragan Bozanovic
  • 21,631
  • 4
  • 36
  • 100
  • Thanks @Dragan! This is a great answer! Now I'm ok with unproxy method, but later I will definitely use the visitor pattern, because it looks like this is the nicest way. – Csaba Sep 28 '15 at 09:33
2

What i understood is, since you are getting the proxy object, which should be of type VehicleEntity, and target object in that proxy object should be CarEntity.

So, in my opinion, you could deproxy the proxy object, so you could get the hold of real object which would be CarEntity.

Then, you could call the special member of CarEntity.

I have found the link, which would deproxy the proxy object in generic manner (since you had a lot of inherited classes)

Converting Hibernate proxy to real object

Hope it help..

Community
  • 1
  • 1
Sanket Bajoria
  • 638
  • 5
  • 15
  • Thank you! It is a help indeed! Especially because there is a JPA 2.0 version of the answers on that link, and I like doing it the old way :D – Csaba Sep 25 '15 at 16:10
  • @Csaba, there is a new option in JPA 2.1 - (TREAT) [http://www.google.sk/url?q=http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/j_treat.htm&sa=U&ved=0CAsQFjAAahUKEwiW3sGarJTIAhWEiiwKHSE-BAg&usg=AFQjCNEI4J85ALYrtu6v8U9gdahecCMDCA] keyword - in the case you are interested in only single subclass. – OndroMih Sep 26 '15 at 09:07