JPA EntityManager查找方法返回代理

Csa*_*aba 3 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
}
Run Code Online (Sandbox Code Playgroud)

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

VehicleEntity vehicleEntity = entityManager.find(VehicleEntity.class, carID);
Run Code Online (Sandbox Code Playgroud)

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

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

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

Dra*_*vic 7

请考虑以下语句序列:

VehicleEntity vehicleEntityProxy = entityManager.getReference(VehicleEntity.class, carID);
VehicleEntity vehicleEntityInitialized = entityManager.find(VehicleEntity.class, carID);
Run Code Online (Sandbox Code Playgroud)

vehicleEntityProxy是一个代理,因为你想要它(通过获得getReference).

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

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

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

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

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

作为一种解决方法,您可以使对象脱毛:

if (entity instanceof HibernateProxy) {
  entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
}
Run Code Online (Sandbox Code Playgroud)

但这很乏味且容易出错(并且不必要地暴露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);
}
Run Code Online (Sandbox Code Playgroud)

现在,而不是:

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
}
Run Code Online (Sandbox Code Playgroud)

你可以做:

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);
Run Code Online (Sandbox Code Playgroud)