Hibernate 一次奇特的异常
反射在 Entity 上的异常
前段时间在线上的 Hibernate 查询出来的 Entity 中反射调用其中 getId
method 时,发生了 java.lang.IllegalArgumentException: object is not an instance of declaring class
异常。
由于是在查询出的一个 List 上依次进行反射,提前将反射的方法缓存了下来,却在某个对象上发生了这种异常。
以下是一份非常简单的复现代码(用 Kotlin 写,但 Java 类似,跟语言无关):
1 |
|
每个 Post 只有 id 和另一个 Post 的对象,在表中也就是主键 id 和一个 post_id 字段。
表中的数据如下:
id | post_id |
---|---|
1 | 2 |
2 | 2 |
复现代码:
1 |
|
调试
首先自然想到是 post1
和 post2
的确属于两个不同的类,链接调试器后发现 post1
的类是 Post
,而 post2
的类是 Post_$$_jvst38d_0
,很明显这是一个被 Hibernate 子类化的类,除了原有的成员变量外,还多了个 handler: JavassistLazyInitializer
变量。

我们知道以下两个行为导致了这个行为:
- Hibernate 会对 LazyFetch 的对象生成代理对象,在 getId 以外的方法上才会真的去数据库中执行查询,因此代理对象必须是一个生成的类,原始类的成员显然无法做到动态进行查询。(注意这一行为仅限 Property-based access,可参考扩展阅读)
- Hibernate 在一个 session 中,被管理的 Entity 总是同一个 Java 对象,不论是被
findOne
,findAll
,或者被其它 Entity 关联的实体。
第一个 findOne 导致 post1 所关联的 id 为 2 的 Post 是一个代理对象,但是为了满足条件 2,Hibernate 必须在第二次 findOne 时返回那个代理对象。由于 post2 的类型是 post1 的子类,显然无法用 post2 的方法在 post1 上反射调用。