我对Java生态系统非常陌生,有时我还在为一些特性而奋斗。
在我的项目中,我有类似的实体和存储库列表:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
class Tournament {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
}
@Entity
class SpecialTournament extends Tournament {
// ... extra fields
}
@Entity
class Match {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@ManyToOne(fetch = FetchType.LAZY)
private Tournament tournament;
private int value;
}
@Entity
class SpecialEvent {
@ManyToOne(fetch = FetchType.LAZY)
private SpecialTournament tournament;
private int value;
private int importantField;
}
interface SpecialEventRepository extends JpaRepository<SpecialEntity, Integer> {
@Query("FROM SpecialEvent WHERE tournament=:tournament AND value=:value ORDER BY importantField")
Iterable<SpecialEvent> findForMatchField(Tournament tournament, int value)
}
@Service
class SpecialEventController {
void doSmth(int matchId, int value) {
var match = matchRepository.findById(matchId);
var specialEvents = specialEventRepository.findForMatchField(match.tournament(), value)
}
}问题是,每当我doSmth时,我就会得到一个类似于Parameter value [Tournament(id=3164)] did not match expected type [org.my.SpecialTournament]的错误。
我发现这是可能的,因为Tournament已经存在于会话中(它加载了匹配),Hibernate试图在构造SpecialEvent实体时重用它。
我通过调整存储库方法来接受SpecialTournament并使用var tournament = entityManager.find(SpecialTournament.class, match.getTournament().getId())来“修复它”,但是我觉得这种方法有问题。另外,我还收到了Hibernate的警告:Narrowing proxy to class org.my.SpecialEntity - this operation breaks ==,所以我认为应该有一个更好的解决方案。尤其是在这种情况下--在大型会话中,很难确定如何应用这种“黑客”。
谢谢!
Update:存储库方法不需要接收锦标赛作为该错误到达的参数。堆栈跟踪显示,当Hibernate试图创建SpecialEvent实体时,错误来自Hibernate内部的深处。出于某种原因,它试图将锦标赛实例分配给SpecialEvent.tournament。
下面是堆栈跟踪:https://pastebin.com/SKWfWmK9
更新2:此外,我试图覆盖.equals(),但是Hibernate代理似乎并不关心我对实体做了什么。
发布于 2022-08-22 13:00:34
因此,在挖掘了一半的互联网后,我发现有时hibernate (令人惊讶地)不够聪明,它需要得到更多的建议。
我目前正在使用Hibernate 5,所以这篇文章给了我完美的解决方案:https://stackoverflow.com/a/217848/1246437
当然,我使用的不是原始的Object作为属性类型,而是域的具体类型,但它似乎仍然像预期的那样工作。
有几个注意事项:
package-info.java)发布于 2022-08-21 23:35:56
我想说这是一种预期的行为:
@Entity
class Match {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@ManyToOne(fetch = FetchType.LAZY)
private Tournament tournament;
private int value;
}FetchType.LAZY over private Tournament tournament使HBN创建一个代理,该代理只存储有关基本实体类型(Tournament)及其标识符的信息。为了将该代理转换为您需要的功能完整的实体,您可能需要调用Hibernate#unproxy,但是这在事务之外是行不通的。
这里:
interface SpecialEventRepository extends JpaRepository<SpecialEntity, Integer> {
@Query("FROM SpecialEvent WHERE tournament=:tournament AND value=:value ORDER BY importantField")
Iterable<SpecialEvent> findForMatchField(Tournament tournament, int value)
}您将得到错误did not match expected type,因为SpecialEvent的tournament字段具有SpecialTournament类型-这里没有魔力。快速而肮脏的解决方案是将findForMatchField方法声明为:
interface SpecialEventRepository extends JpaRepository<SpecialEntity, Integer> {
@Query("FROM SpecialEvent WHERE tournament.id=:tournamentId AND value=:value ORDER BY importantField")
Iterable<SpecialEvent> findForMatchField(Integer tournamentId, int value)
}https://stackoverflow.com/questions/73438644
复制相似问题