如何用Hibernate Restrictions比较两列值?MySQL查询转Criteria遇阻
嘿,我帮你搞定这个Hibernate Criteria转换的问题!你的原SQL其实是在给每个abc记录找出对应的最新xyz条目(通过时间戳排序,时间戳相同就按ID取大的)对吧?下面一步步给你拆解解决方案:
一、先理清原SQL的核心逻辑
你的SQL用左连接b2的方式,本质是判断当前b是不是abc下排序最靠前的那条记录——如果没有比b时间戳更新、或者时间戳相同但ID更大的b2存在,那b就是我们要的目标记录。搞懂这个逻辑,转换起来就清晰多了。
二、Hibernate Criteria 实现代码
假设你的实体类是这样的:
Abc:对应表abc,包含id属性,以及关联Xyz的集合(比如@OneToMany(mappedBy = "abc") private List<Xyz> xyzList;)Xyz:对应表xyz,包含id、timestamp、speed属性,以及关联Abc的字段(@ManyToOne private Abc abc;)
直接上可运行的Criteria代码:
Session session = sessionFactory.getCurrentSession(); // 主查询从Abc实体开始,别名设为a Criteria criteria = session.createCriteria(Abc.class, "a"); // 关联第一个Xyz表(对应原SQL的b) criteria.createAlias("a.xyzList", "b"); // 左连接第二个Xyz表(对应原SQL的b2),这里直接传入连接条件 criteria.createAlias( "a.xyzList", "b2", JoinType.LEFT_OUTER_JOIN, // 拼接原SQL中的on条件:a.id = b2.abc_id AND (...) Restrictions.and( Restrictions.eqProperty("b2.abc.id", "a.id"), Restrictions.or( // 对应b.timestamp < b2.timestamp Restrictions.gtProperty("b2.timestamp", "b.timestamp"), Restrictions.and( // 对应b.timestamp = b2.timestamp AND b.id < b2.id Restrictions.eqProperty("b2.timestamp", "b.timestamp"), Restrictions.gtProperty("b2.id", "b.id") ) ) ) ); // 过滤掉存在b2的记录——没有匹配到b2,说明b是当前abc下的最新条目 criteria.add(Restrictions.isNull("b2.id")); // 投影需要返回的字段,同时设置别名方便转DTO criteria.setProjection(Projections.projectionList() .add(Projections.property("a.id"), "abcId") .add(Projections.property("b.id"), "xyzId") .add(Projections.property("b.timestamp"), "timestamp") .add(Projections.property("b.speed"), "speed") ); // 如果需要转换成自定义DTO类,用ResultTransformer criteria.setResultTransformer(Transformers.aliasToBean(YourCustomDto.class)); // 执行查询 List<YourCustomDto> resultList = criteria.list();
关键知识点说明
- 比较两列的值:Hibernate的
Restrictions提供了eqProperty、gtProperty、ltProperty等方法,专门用来做列与列之间的比较,完美解决你提到的“如何用Restrictions比较两列”的问题。比如gtProperty("b2.timestamp", "b.timestamp")就等价于SQL里的b.timestamp < b2.timestamp。 - 复杂左连接条件:通过
createAlias的第四个参数,直接传入连接条件的Restrictions组合,就能实现原SQL中left join xyz b2 on (...)的复杂逻辑,不需要额外的原生SQL拼接。
三、更直观的替代方案:用HQL实现
如果觉得Criteria写起来有点绕,HQL会更贴近原生SQL的写法,逻辑一眼就能看懂:
SELECT a.id as abcId, b.id as xyzId, b.timestamp as timestamp, b.speed as speed FROM Abc a JOIN a.xyzList b LEFT JOIN a.xyzList b2 ON b2.abc.id = a.id AND (b.timestamp < b2.timestamp OR (b.timestamp = b2.timestamp AND b.id < b2.id)) WHERE b2.id IS NULL
执行代码也很简单:
Query query = session.createQuery(hql); query.setResultTransformer(Transformers.aliasToBean(YourCustomDto.class)); List<YourCustomDto> resultList = query.list();
四、注意事项
- 确保实体类的关联关系配置正确,比如
Xyz中的abc字段要正确映射abc_id外键,否则关联查询会出错。 - 如果你用的是Hibernate 5及以上版本,
JoinType需要导入org.hibernate.jpa.criteria.JoinType或者org.hibernate.sql.JoinType,根据你的依赖版本调整。 - 如果后续需要加分页、动态条件等操作,Criteria的灵活性会更强;但如果只是实现固定逻辑,HQL的可读性会更好。
内容的提问来源于stack exchange,提问作者Maulik Kakadiya
相关产品推荐
相关产品推荐

