Hibernate:通过一对多+多对一关联排序子实体
嘿,我明白你的困扰了——你想从Contact端对关联的ContactList子实体排序,但排序字段是在ContactList关联的ReviewList里,直接用@OrderBy("created_on DESC")肯定无效,因为JPA会在ContactList里找这个属性,而它其实属于ReviewList。下面给你两种靠谱的解决办法:
1. 数据库层面排序(推荐):用嵌套属性的@OrderBy
主流JPA提供者(比如Hibernate)都支持通过关联属性路径来指定排序字段,你只需要把@OrderBy的参数改成从ContactList到ReviewList的属性路径,再加上目标字段就行。
举个例子,假设你的实体是这么定义的:
ContactList里有private ReviewList reviewList;(对应@ManyToOne关联)ReviewList里有private LocalDateTime createdOn;(对应数据库的created_on列)
那在Contact的@OneToMany注解里这么写就对了:
@OneToMany(mappedBy = "contact") @OrderBy("reviewList.createdOn DESC") // 这里用的是实体属性名,不是数据库列名! private List<ContactList> contactLists;
这种方式是让数据库在查询时直接完成排序,性能更好,适合数据量较大的场景。
划重点:一定要用实体类中的属性名(比如
reviewList、createdOn),别用数据库的列名(review_list、created_on),不然JPA会找不到属性报错。
2. 内存层面排序:用SortedSet自定义比较器
如果你需要更灵活的排序逻辑,或者数据量不大,可以用SortedSet代替List,自己写个比较器来基于ReviewList的createdOn排序:
@OneToMany(mappedBy = "contact") private SortedSet<ContactList> contactLists = new TreeSet<>( Comparator.comparing( ContactList::getReviewList, Comparator.comparing(ReviewList::getCreatedOn).reversed() ) );
这种方式是把所有ContactList数据加载到内存后再排序,好处是能自定义复杂逻辑,但数据量大的话会占内存,性能不如数据库排序。
提醒一下:要确保
ContactList关联的ReviewList不会为null,不然调用getCreatedOn()会空指针,你可以在比较器里加个null值处理,比如用Comparator.nullsFirst()或者nullsLast()。
为啥你原来的写法没用?
你之前写的@OrderBy("created_on DESC"),JPA会去ContactList里找叫created_on的属性,但这个属性是在ReviewList里的,而且你没指定关联路径,JPA根本不知道要去关联实体里找,所以自然不会生效啦。
内容的提问来源于stack exchange,提问作者ScottM

