单向ManyToMany关联查询User重复返回,求替代废弃Criteria的方案
解决单向ManyToMany关联下用户重复返回的问题
嘿,这问题我之前也碰到过,给你几个靠谱的解决方案,都能避开废弃的Criteria API:
方案1:用JPQL的DISTINCT关键字(推荐,JPA标准)
因为LEFT JOIN FETCH会产生笛卡尔积(一个用户对应多个角色就会生成多条重复记录),直接在JPQL里加上DISTINCT就能让Hibernate自动帮你去重,同时还能保留已经加载好的roles集合,不会触发N+1查询:
public List<User> listUsers() { List<User> resultList = manager.createQuery("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.roles", User.class) .getResultList(); return resultList; }
这个方案最简洁,完全符合JPA标准,不需要依赖Hibernate的私有API,兼容性最好。
方案2:使用Hibernate原生的DistinctRootEntityResultTransformer
如果你项目已经深度依赖Hibernate,也可以用这个专门替代废弃的Criteria.DISTINCT_ROOT_ENTITY的转换器,它并没有被废弃:
import org.hibernate.transform.DistinctRootEntityResultTransformer; public List<User> listUsers() { List<User> resultList = manager.createQuery("SELECT u FROM User u LEFT JOIN FETCH u.roles") .unwrap(org.hibernate.query.Query.class) .setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE) .getResultList(); return resultList; }
注意这里要导入Hibernate的Query和对应的转换器,属于Hibernate专属方案,适合不需要跨JPA provider的场景。
方案3:JPA实体图(Entity Graph)+ DISTINCT
如果想更规范地控制关联加载,可以用JPA标准的实体图来指定要加载roles集合,再配合DISTINCT去重:
首先在User实体类上定义实体图:
@Entity @NamedEntityGraph(name = "User.withRoles", attributeNodes = @NamedAttributeNode("roles")) public class User { // 你的用户字段 @ManyToMany private List<Role> roles; // getter、setter等 }
然后查询时使用实体图并添加DISTINCT:
public List<User> listUsers() { List<User> resultList = manager.createQuery("SELECT DISTINCT u FROM User u", User.class) .setHint("javax.persistence.loadgraph", manager.getEntityGraph("User.withRoles")) .getResultList(); return resultList; }
这个方案也是JPA标准,实体图的方式让关联加载的意图更清晰,适合需要统一管理关联加载策略的项目。
内容的提问来源于stack exchange,提问作者Ilya Y
相关产品推荐
相关产品推荐

