JPA EntityManager查找方法返回代理

时间:2015-09-25 12:09:26

标签: java hibernate jpa

我有两个实体。一个继承自另一个。

示例:

@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
}

当我在EntityManager上使用现有汽车的id调用find()时:

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

我找回了一个代理对象,但是我需要访问CarEntity类的特殊方法,因为我想设置一些新的参数。

有人可以帮助我,我该怎么做?

当然这只是一个示例问题。更具体地说:在我调用find之后我正在检查返回对象的实例,如果它是“CarEntity”我设置参数,如果不是我什么都不做。我在“CarEntity”旁边有更多的继承类,我和以前一样做了同样的程序。我知道我可以用特定对象类的“find”来解决这个问题,但是我必须多次调用“find”,当我正在寻找任何“VehicleEntity”时,我总是对实际对象感兴趣所以最好的将是全球解决方案。

2 个答案:

答案 0 :(得分:7)

考虑以下语句序列:

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

vehicleEntityProxy是代理,因为您希望它(通过getReference获得)。

在第二个语句中,您需要一个初始化的实例,因此您将使用find获取它:

  

按主键查找。搜索指定类的实体和   首要的关键。 如果实体实例包含在持久性中   上下文,从那里返回。

因此,find将检查实例是否已经在持久化上下文中,它将确定,因为已经在第一个语句中创建了代理,它将初始化代理(因为它委托给永远不会返回未初始化实例的Hibernate Session.get)并返回代理。

如果代替第一个语句,您已经加载了一个与VehicleEntity carID id具有懒惰一对一关联的其他实体,则会发生类似的事情。然后创建代理来代替真实实体实例,并且当找到相同id的实体时,将从find方法返回该代理(并且如果尚未初始化则该代理)。

所有这些进一步暗示 instanceof在使用Hibernate代理时不是你的朋友(撇开一般来说,当谈到好的OOP代码时,它不是你的朋友)。

作为一种解决方法,您可以对对象进行去除:

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

但这很乏味且容易出错(并且不必要地暴露Hibernate特定的API)。此外,您可能还需要为所有关联对象执行此操作,因为它们也可以是代理。

更好的方法是使用经过验证的OOP结构和模式,例如Visitor模式:

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);
}

现在,而不是

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
}

你可以这样做:

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);

答案 1 :(得分:2)

我理解的是,因为您正在获取代理对象,该对象应该是VehicleEntity类型,并且该代理对象中的目标对象应该是CarEntity。

所以,在我看来,你可能会使代理对象变得黯然失色,所以你可以获得CarEntity的真实对象。

然后,你可以打电话给CarEntity的特别成员。

我找到了链接,它会以通用方式对代理对象进行deproxy(因为你有很多继承的类)

Converting Hibernate proxy to real object

希望有所帮助......