通过hql控制hibernate一对多的集合属性的排序
程序员文章站
2022-04-16 23:36:58
...
【问题】
- 当hibernate实体中存在一对多关系集合时(比如:Person 1->* Addr ) ,无法完全使用hql来控制集合属性内部的排序。
- 假设我们想addrs集合按照addr.id排序,理所当然的想到硬编码@OrderBy("id"),可是这样的话无论hql中是否已经有orderby语句,最后都会追加一句orderby addrs.id,这样在一些我们不想将addrs按照id排序的场景下就会有问题(hibernate并不会检测你是否在hql定制了排序,始终默认添加),这不是我们想要的
- 但是如果不加@OrderBy("id"),那么产生的集合底层是hashset(不要问我怎么知道的都是泪= =。 调试了好久,其实一开始就应该想到)
【具体解决问题的过程】
- 首先想确定到底是不是hibernate底层返回的顺序就是不对的(因为当时没去看返回的PersistCollection的私有变量 = =,如果看了的话,就可以跳过下面的很大一部分解析过程)。看query,一路追查到 org.hibernate.loader.Loader.readCollectionElement,发现有日志可以打(log4j.logger.org.hibernate.loader=DEBUG),开启后可以清楚看到hibernate读取每一行的过程,同时也得到结论,读取的时候顺序是按照我hql的排序规则,没问题。
- 在以上调试过程中发现org.hibernate.mapping.Set.getDefaultCollectionType 方法
public class Set extends Collection { public CollectionType getDefaultCollectionType() { if ( isSorted() ) { return TypeFactory.sortedSet( getRole(), getReferencedPropertyName(), isEmbedded(), getComparator() ); } else if ( hasOrder() ) { return TypeFactory.orderedSet( getRole(), getReferencedPropertyName(), isEmbedded() ); } else { return TypeFactory.set( getRole(), getReferencedPropertyName(), isEmbedded() ); } } //...以下是集成父类Collection中的方法 public boolean hasOrder() { return orderBy!=null || manyToManyOrderBy!=null; } }
好像瞬间明白了些什么。。没错,就是直接写@orderby注解就是linkedhashset了,这样就能保证数集合类根据hql的规则,也就是实际返回数据的顺序来进行排序。。唉好坑~最终解决方法实在是太简单了,绕了一大圈。
【结论以及解决方法】
-
@Entity @Table(name = "...") public class Person{ @OneToMany(mappedBy = "person") @OrderBy //***没错就是这行,只写注解,不写参数!!!*** public Set<Addr> getAddrs() { return addrs; } }
PS: 大家可能要问这样排序好像不对吧!没错,我省略了主表的排序语句,这个大家记得自己补上吧,举例就是 select p from Persion p left join fetch p.addrs a order by p.id,a.id 就是这样,不要忘了主表的排序哟。
上一篇: Qt样式表使用