为什么在将延迟获取与 quarkus panache 一起使用时向下转换会抛出异常?
Why down-casting throws an exception when using lazy fetch with quarkus panache?
我有一个实体层次结构:ATeamEntity
和 BTeamEntity
扩展了 BaseTeamEntity
和包含 BaseTeamEntity
的 UserEntity
。
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 将使用团队的代理而不是真正的团队对象填充该字段。该代理本质上是一个由 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()
调用后,team
和ateam
指的是两个不同的proxy
实例,它们都委托给相同的底层 ATeamEntity
实例。但是,第二个代理具有不同的接口,您可以检查仅适用于该接口的 ateam.aData
等字段。 (请注意,如果您将 ID 属性 映射到 field access,team.getId()
将触发 SELECT
)
我有一个实体层次结构:ATeamEntity
和 BTeamEntity
扩展了 BaseTeamEntity
和包含 BaseTeamEntity
的 UserEntity
。
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 将使用团队的代理而不是真正的团队对象填充该字段。该代理本质上是一个由 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()
调用后,team
和ateam
指的是两个不同的proxy
实例,它们都委托给相同的底层 ATeamEntity
实例。但是,第二个代理具有不同的接口,您可以检查仅适用于该接口的 ateam.aData
等字段。 (请注意,如果您将 ID 属性 映射到 field access,team.getId()
将触发 SELECT
)