为什么在将延迟获取与 quarkus panache 一起使用时向下转换会抛出异常?

Why down-casting throws an exception when using lazy fetch with quarkus panache?

我有一个实体层次结构:ATeamEntityBTeamEntity 扩展了 BaseTeamEntity 和包含 BaseTeamEntityUserEntityBaseTeamEntityUserEntity 扩展 BaseEntity BaseEntity 扩展 PanacheEntityBase.

我在 UserEntity 中定义了以惰性方式获取 BaseTeamEntity 我正在尝试执行这样的沮丧:

UserEntity user = UserEntity.findById(1L);
BaseTeamEntity team = user.team;
ATeamEntity ateam = (ATeamEntity)team; <--
Long a = ateam.aData;

当我这样做时出现异常:

java.lang.ClassCastException: class ***.***.entities.team.BaseTeamEntity$HibernateProxy$YcDcMejE cannot be cast to class ***.***.entities.team.ATeamEntity (***.***.entities.team.BaseTeamEntity$HibernateProxy$YcDcMejE and ***.***.entities.team.ATeamEntity are in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @4f636cba)

如果我想直接向下转换而不是像这样通过 UserEntity

BaseTeamEntity team = BaseTeamEntity.findById(1L);
ATeamEntity ateam = (ATeamEntity)team;  <--
Long a = ateam.aData;

它似乎有效。

当我以急切的方式在 UserEntity 中获取 BaseTeamEntity 时,两种方式似乎都有效。

我不明白为什么... 有人可以帮我理解为什么吗?

以下是类

的高级结构
@MappedSuperclass
public class BaseEntity extends PanacheEntityBase {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;

}
@Entity
@Table(name = "users")
public class UserEntity extends BaseEntity {

    @ManyToOne(fetch = FetchType.LAZY)  <------------ throws exception 
    @ManyToOne(fetch = FetchType.EAGER) <--------- working
    @JsonbTransient
    public BaseTeamEntity team;
    
}
@Inheritance(strategy = InheritanceType.JOINED)
@Entity
@Table(name = "teams")
@DiscriminatorColumn(name = "team_type", discriminatorType = DiscriminatorType.STRING)
public abstract class BaseTeamEntity extends BaseEntity {
    
    public String sharedData;

}
@Entity
@Table(name = "teams_A")
@DiscriminatorValue("team_A")
@PrimaryKeyJoinColumn
public class ATeamEntity extends BaseTeamEntity {

    public Long aData;

}
@Entity
@Table(name = "teams_B")
@DiscriminatorValue("team_B")
@PrimaryKeyJoinColumn
public class BTeamEntity extends BaseTeamEntity {

    public Integer bData;

}

当延迟获取用户的团队时,Hibernate 将使用团队的代理而不是真正的团队对象填充该字段。该代理本质上是一个由 Hibernate 动态生成的 class 的空实例。

只要您调用团队对象的方法(例如 getAData()),Hibernate 就会通过执行相应的 select 查询来初始化代理。

在执行该查询之前,Hibernate 不知道类型(ATeamEntity 或 BTeamEntity),它只知道它是 BaseTeamEntity,因此代理 class 扩展 BaseTeamEntity 并且转换失败。

当将获取类型更改为 eager 时,您要求 Hibernate 立即获取团队并且它不需要创建代理,因为它已经知道类型。

代理的优点之一是您可能永远不需要访问团队对象,在这种情况下您可以保存查询。

除了@Guillaume 的回答,我想说的是要执行代理安全的类型转换,您应该这样做:

UserEntity user = UserEntity.findById(1L);
BaseTeamEntity team = user.team;
ATeamEntity ateam = entityManager.getReference(ATeamEntity.class, team.id);

assertTrue(team != ateam); // !!!

Long a = ateam.aData;

getReference()调用后,teamateam指的是两个不同的proxy 实例,它们都委托给相同的底层 ATeamEntity 实例。但是,第二个代理具有不同的接口,您可以检查仅适用于该接口的 ateam.aData 等字段。 (请注意,如果您将 ID 属性 映射到 field accessteam.getId() 将触发 SELECT