疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Hibernate3.2.3之后的隐式关联的改变


  很多人将Hibernate从3.2.2升级到更高版本,
然后再执行HQL查询时,可能出现如下异常:

  1. org.hibernate.QueryException: illegal attempt to dereference collection
复制代码 通常出现这个异常是由类似与如下HQL语句所引起的:
  1. from Person p where p.myEvents.title = :eventTitle
复制代码 在上面的HQL语句中,Person的关联实体myEvents是一个集合,而不直接是一个MyEvent实体。

在Hibernate3.2.2以前的版本,Hibernate会对关联实体自动使用隐式的inner join,
也就是说如下SQL语句不会有任何问题:
  1. from Person p where p.myEvents.title = :eventTitle
复制代码 从Hibernate3.2.3以后,Hibernate改变了这种隐式的inner join的策略
对于如下这条语句:
  1. from Person p where p.myEvents.title = :eventTitle
复制代码 如果myEvents是普通组件属性,或单个的关联实体,则Hibernate会自动生成隐式的inner join
如果myEvents是也一个集合,那么对不起!系统将会出现
  1. org.hibernate.QueryException: illegal attempt to dereference collection
复制代码 异常。
据Hibernate官方说法:
这样可以让这使得隐含关联更具确定性(原文:This makes implicit joins more deterministic )。
这很令人FT,
以前我一直对HQL一直津津乐道的简洁性又少了一个。

这里涉及到一个形而上的命题:
技术的设计到底是原则性更重要?还是简洁性更重要?
——或许只有时间才能回答这个问题。

为了改进这个问题,Hibernate推荐将上面的HQL语句写成:
  1. from Person p inner|left|right|full join p.myEvents e where e.title = :eventTitle
复制代码
这条HQL语句似乎更像SQL语句了。
而且这条HQL语句会返回一个集合,集合元素是Person实体和MyEvent实体所组成的数组。
实际上面HQL语句会转换selct person.* , my_event.* from person inner|left|right|full join my_event...
这样选择出来的每条结果实际实际上包含了person表的所有列、 my_event表的所有列,
然后Hibernate将每条记录转换成一个Person和MyEvent组成的数组,
最后将这些数组封装成一个List后返回。
如果只想获取Person组成的集合,则需要改写成:
  1. select p from Person p inner|left|right|full join p.myEvents e where e.title = :eventTitle
复制代码
但上面语句有可能返回多个完全相同的Person对象,想一想SQL连接查询的结果就知道为什么。
如果只想得到Person集合,且元素不重复,应该改为如下HQL语句:
  1. select distinct p from Person p inner|left|right|full join p.myEvents e where e.title = :eventTitle
复制代码
呜呼——
看来那些想升级Hibernate的企业用户都需要考虑一下了:这将导致多少DAO类需要改写?
Hibernate为什么总喜欢搞这样的事情:
版本一升级,以前的代码就冒出了大量错误。


如果你还怀念以前如下风格的HQL语句:
  1. from Person p where p.myEvents.title.aa.xxx = :eventTitle
复制代码
你只能退回去使用Hibernate3.2.2了。