如何将按SignLogUUID分组取最大id的SQL改写为JPA Criteria实现
JPA 实现按SignLogUUID分组取最新记录方案
需求说明
需要实现的查询逻辑等价于以下原生SQL:
SELECT * FROM webamdb.sign_log WHERE id IN (SELECT MAX(id) AS id FROM webamdb.sign_log GROUP BY SignLogUUID) ORDER BY CreateTime;
即按SignLogUUID分组取每组最大id(即每组最新记录),查询对应全量字段后按创建时间排序。以下优先提供要求的JPA Criteria写法,同时补充简化实现方案。
1. 优先实现:JPA Criteria API 写法
该写法为JPA标准类型安全实现,支持动态条件拼接,适合复杂业务场景。需要注入EntityManager使用,代码示例如下:
import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Subquery; import java.util.List; // 可写在Service层或自定义Repository实现类中 @PersistenceContext private EntityManager entityManager; public List<SignLog> listLatestGroupBySignLogUuid() { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); // 构建主查询:查询SignLog实体全量字段 CriteriaQuery<SignLog> mainQuery = cb.createQuery(SignLog.class); Root<SignLog> mainRoot = mainQuery.from(SignLog.class); // 构建子查询:按signLogUUID分组,取每组最大id Subquery<Long> maxIdSubQuery = mainQuery.subquery(Long.class); Root<SignLog> subRoot = maxIdSubQuery.from(SignLog.class); maxIdSubQuery.select(cb.max(subRoot.get("id"))) .groupBy(subRoot.get("signLogUUID")); // 组装主查询逻辑:id在子查询结果集中,按createTime升序排序(需要倒序可替换为cb.desc) mainQuery.select(mainRoot) .where(cb.in(mainRoot.get("id")).value(maxIdSubQuery)) .orderBy(cb.asc(mainRoot.get("createTime"))); return entityManager.createQuery(mainQuery).getResultList(); }
注:如果使用javax.persistence包(Spring Boot 2.x版本),将上述导入包路径替换为javax.persistence对应类即可。
2. 简化实现:Spring Data JPA JPQL 写法
如果查询逻辑固定、不需要动态拼接条件,可以直接在Spring Data JPA的Repository接口中通过@Query注解写JPQL,无需手动操作EntityManager,代码更简洁:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; public interface SignLogRepository extends JpaRepository<SignLog, Long> { @Query("SELECT s FROM SignLog s " + "WHERE s.id IN (SELECT MAX(s2.id) FROM SignLog s2 GROUP BY s2.signLogUUID) " + "ORDER BY s.createTime ASC") List<SignLog> listLatestGroupBySignLogUuid(); }
注意事项
- 所有JPA查询中引用字段时,传入的是实体类的驼峰命名字段,而非数据库下划线列名。例如实体中定义的字段是
signLogUUID、createTime,不要直接传入数据库列名SignLogUUID、CreateTime,否则会抛出属性找不到的异常。 - 你当前实体中
WebInfo关联配置为FetchType.LAZY懒加载,若查询后需要访问关联的webInfo属性,请在事务上下文内操作,避免懒加载异常;如果需要查询时直接加载关联数据,JPQL可追加LEFT JOIN FETCH s.webInfo,Criteria写法可通过mainRoot.fetch("webInfo", JoinType.LEFT)实现。 - 若必须使用原生SQL,可在
@Query注解中添加nativeQuery = true参数,直接写入你提供的原生SQL即可,但该方式会牺牲数据库兼容性,非必要不推荐。
内容的提问来源于stack exchange,提问作者Jessie
相关产品推荐
相关产品推荐

