欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

通过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  就是这样,不要忘了主表的排序哟。