[英]Why down-casting throws an exception when using lazy fetch with quarkus panache?
我有一个实体层次结构: ATeamEntity
和BTeamEntity
扩展BaseTeamEntity
和包含UserEntity
的BaseTeamEntity
。 BaseTeamEntity
和UserEntity
扩展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 将使用团队的代理而不是真实团队 object 填充该字段。 该代理本质上是一个由 Hibernate 动态生成的 class 的空实例。
只要您调用团队中的方法 object(例如getAData()
) Hibernate 将通过执行相应的 select 查询来初始化代理。
在执行该查询之前,Hibernate 不知道类型(ATeamEntity 或 BTeamEntity),它只知道它是 BaseTeamEntity,因此代理 class 扩展了 BaseTeamEntity 并且转换失败。
将获取类型更改为 eager 时,您要求 Hibernate 立即获取团队,它不需要创建代理,因为它已经知道类型。
代理的优点之一是您可能永远不需要访问团队 object,在这种情况下您可以保存查询。
除了@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()
调用之后, team
和ateam
引用两个不同的代理实例,这两个代理实例都委托给同一个底层ATeamEntity
实例。 但是,第二个代理具有不同的接口,您可以检查仅适用于该接口的ateam.aData
等字段。 (请注意,如果您使用 map 具有字段访问权限的 id 属性,则team.getId()
将触发SELECT
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.