【问题标题】:JPA EntityManager find method returns proxy [duplicate]JPA EntityManager查找方法返回代理[重复]
【发布时间】:2015-12-23 06:29:09
【问题描述】:

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

例子:

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

当我使用现有汽车的 ID 在 EntityManager 上调用 find() 时,如下所示:

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

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

有人可以帮我怎么做吗?

当然,这只是一个示例问题。更具体地说:在我调用 find 之后,我正在检查返回对象的实例,如果它是“CarEntity”我设置参数,如果不是我不做任何事情。除了“CarEntity”之外,我还有更多的继承类,并且我执行与以前相同的过程。我知道我可以通过特定对象类的“find”来解决这个问题,但是我必须多次调用“find”,当我在寻找任何“VehicleEntity”时,我总是对实际对象感兴趣所以最好的办法是全球解决方案。

【问题讨论】:

  • 我认为这是一个 Hibernate 问题(WildFly 使用的默认 JPA 实现),而不是 WildFly 问题。
  • 不清楚为什么您认为您不能使用特定类型作为 find 方法的参数。你能详细说明一下吗?
  • 感谢指正!这是我在这里的第一个问题,对错误感到抱歉。正如我所写的,我可以使用具有特定类型的 find 方法,但是我必须调用 find 20-30 次(因为继承的类很多)。这就是为什么我正在寻找一个全局解决方案,在祖先类型上只使用一次 find,然后确定继承的类型。
  • 您确定要找回代理吗?还是VehicleEntityVehicleEntity 本身的其他实例(因为它不是抽象的)?
  • 你有什么理由不改变你的模型来依赖多态性呢?您的模型本身可以确定应该使用哪种逻辑,或者即使被代理,这种方法也可以返回其类类型。

标签: java hibernate jpa


【解决方案1】:

考虑以下语句序列:

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

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

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

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

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

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

所有这一切进一步暗示 instanceof 在使用 Hibernate 代理时不是您的朋友(撇开在良好的 OOP 代码方面它通常不是您的朋友)。 p>

作为一种解决方法,您可以代理该对象:

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

【讨论】:

  • 谢谢@Dragan!这是一个很好的答案!现在我可以使用 unproxy 方法,但以后我肯定会使用访问者模式,因为看起来这是最好的方法。
【解决方案2】:

我的理解是,因为你得到的代理对象应该是 VehicleEntity 类型的,并且该代理对象中的目标对象应该是 CarEntity。

因此,在我看来,您可以解除代理对象的代理,这样您就可以获得 CarEntity 的真实对象。

然后,您可以调用 CarEntity 的特殊成员。

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

Converting Hibernate proxy to real object

希望对你有帮助。

【讨论】:

猜你喜欢
  • 2016-09-21
  • 2012-07-31
  • 2019-03-30
  • 2013-06-24
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 2021-09-14
  • 1970-01-01
相关资源
最近更新 更多