Kaw*_*awu 8 hibernate jpa lazy-loading classcastexception hibernate-4.x
我为地理区域(如大洲,国家,州等)提供了以下JOINED继承根实体:
@Entity
@Table(name = "GeoAreas")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class GeoArea implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
protected Integer id;
@Column
protected String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id", referencedColumnName = "id")
protected GeoArea parent;
...
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,地理区域有一个简单的自动ID,如PK,名称以及与其父级的关系(自引用).请注意parent映射为的地理区域关系FetchType.LAZY.在数据库中,FK parent_id正在NOT NULL使关系成为可选的.默认为@ManyToOneis optional = true,因此映射似乎是正确的.
子类定义了其他属性,实际上并不重要.数据库中的数据正确链接,因为可以通过JPQL列出地理区域而没有问题(FetchType.EAGER映射略有不同parent,请参见文本结尾):

每一行都是以下的实例:
public class ArenaListViewLine
{
private final Integer arenaId;
private final String arenaName;
private final String arenaLabel;
private final Boolean hasPostAddress;
...
public ArenaListViewLine(Arena arena, Boolean hasPostAddress)
{
// init fields
...
List<String> continentNames = new ArrayList<String>();
List<String> countryNames = new ArrayList<String>();
List<String> regionNames = new ArrayList<String>();
List<String> stateNames = new ArrayList<String>();
List<String> districtNames = new ArrayList<String>();
List<String> clubShorthands = new ArrayList<String>();
// add each geo area to list whose club is a user of an arena (an arena has several usages)
// in border areas of states two clubs from different states might use an arena (rare!)
// this potentially adds several states to an entry in the arena list (non in DB yet)
for ( Usage us : usages )
{
Club cl = us.getClub();
// a club is located in one district at all times (required, NOT NULL)
District di = cl.getDistrict();
System.out.println("arena = " + arenaName + ": using club's district parent = " + di.getParent());
State st = (State)di.getParent(); // ClassCastException here!
...
}
...
}
Run Code Online (Sandbox Code Playgroud)
当父映射为LAZY运行列表查询时,我得到以下异常:
...
Caused by: org.hibernate.QueryException: could not instantiate class [com.kawoolutions.bbstats.view.ArenaListViewLine] from tuple
at org.hibernate.transform.AliasToBeanConstructorResultTransformer.transformTuple(AliasToBeanConstructorResultTransformer.java:57) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:95) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.loader.hql.QueryLoader.getResultList(QueryLoader.java:438) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2279) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.loader.Loader.list(Loader.java:2274) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:470) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:355) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:196) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1115) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:252) [hibernate-entitymanager-4.0.0.Final.jar:4.0.0.Final]
... 96 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) [:1.7.0_02]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) [:1.7.0_02]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) [:1.7.0_02]
at java.lang.reflect.Constructor.newInstance(Unknown Source) [:1.7.0_02]
at org.hibernate.transform.AliasToBeanConstructorResultTransformer.transformTuple(AliasToBeanConstructorResultTransformer.java:54) [hibernate-core-4.0.0.Final.jar:4.0.0.Final]
... 106 more
Caused by: java.lang.ClassCastException: com.kawoolutions.bbstats.model.GeoArea_$$_javassist_273 cannot be cast to com.kawoolutions.bbstats.model.State
at com.kawoolutions.bbstats.view.ArenaListViewLine.<init>(ArenaListViewLine.java:92) [classes:]
... 111 more
Run Code Online (Sandbox Code Playgroud)
堆栈跟踪之前的println是:
13:57:47,245 INFO [stdout] (http--127.0.0.1-8080-4) arena = Joachim-Schumann-Schule: using club's district parent = com.kawoolutions.bbstats.model.State@2b60693e[id=258,name=Hesse,isoCode=HE,country=com.kawoolutions.bbstats.model.Country@17f9976b[id=88,name=Germany,isoCode=DE,isoNbr=276,dialCode=<null>]]
Run Code Online (Sandbox Code Playgroud)
println清楚地说父母是国家的一个例子,但它不能被投到国家?我不知道...
将GeoArea.parent的FetchType更改为EAGER时,一切正常(参见上图).
我究竟做错了什么?LAZY提示有什么问题?
谢谢
PS:我使用的是Hibernate 4.0.0.Final,映射都是标准的JPA,服务器是JBoss AS 7.
Rob*_*ier 17
延迟加载的问题是Hibernate将动态生成延迟加载的对象的代理.虽然起初这似乎是一个实现延迟加载的好主意,但开发人员必须意识到对象可以是Hibernate代理.我认为这是漏洞抽象的一个很好的例子.在您的情况下,调用的返回值di.getParent()是一个代理对象,由Hibernate使用的javassist库生成.
这样的Hibernate代理会导致与转换,instanceof和调用相关的问题equals(),hashCode()如果你没有为你的实体实现这些方法.
阅读此问题和答案,其中显示了如何使用标记接口HibernateProxy将代理转换为真实对象.另一种选择是在这种情况下采用急切加载.
顺便说一句,请考虑放弃"匈牙利符号";-)
编辑解决评论中的问题:
是否有便携式(JPA)方法来实现这一目标?
从来没听说过.我将来自其他QA的转换逻辑从接口后面抽象出来,并提供一个替代的noop实现使用JBoss 7和CDI,你可以切换到那个甚至没有重新编译.
可以自动化吗?
有可能使用AspectJ.您可以尝试around为所有返回其他实体的实体的getter 写入调用建议.该建议将检查基础字段是否为Hibernate代理,如果是,则应用转换,然后返回转换结果.因此,在调用getter时,您总是可以获得真实对象,但仍然可以从延迟加载中获益.你应该彻底测试一下,不过......
| 归档时间: |
|
| 查看次数: |
7796 次 |
| 最近记录: |