对于Hibernate或JPA而言,这感觉就像一个微不足道的用例,但我已经挣扎了几天才能让它发挥作用。
我有一个position
实体类,其中包含latitude
,longitude
和updateTime
字段(以及其他字段)。我想计算这三个字段的不同组合的数量,而忽略其他字段。在SQL中,这是微不足道的:
SELECT COUNT(*) FROM (SELECT DISTINCT LONGITUDE, LATITUDE, UPDATE_TIME FROM POSITION) AS TEMP;
从我的应用程序的其余部分抽象myh数据库实现非常重要,因为不同的用户可能希望使用不同的数据库引擎。 (哎呀我用h2进行测试,mariadb用于本地制作......)
我一直在尝试使用Hibernate或JPA语法将此SQL转换为Java代码,但我无法弄清楚如何。
编辑 - 这与我能够使用JPA一样接近(参考:https://en.wikibooks.org/wiki/Java_Persistence/Criteria)
public long getCountDistinctInFlightPositions() {
Session session = sessionFactory.openSession();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<Tuple> innerQuery = criteriaBuilder.createTupleQuery();
Root<Position> position = innerQuery.from(Position.class);
innerQuery.multiselect(
position.get("longitude"),
position.get("latitude"),
position.get("updateTime")
);
// The method countDistinct(Expression<?>) in the type CriteriaBuilder is not applicable for the arguments (CriteriaQuery<Tuple>)
criteriaBuilder.countDistinct(innerQuery);
return 1;
}
答案 0 :(得分:1)
你可以这样做:
CriteriaQuery<Long> countQuery = cb.createQuery( Long.class );
Root<Position> root = countQuery.from( Position.class );
countQuery.select( cb.count( root.get( "id" ) ) );
Subquery<Integer> subQuery = countQuery.subquery( Integer.class );
Root<Position> subRoot = subQuery.from( Position.class );
subQuery.select( cb.min( subRoot.get( "id" ) ) );
subQuery.groupBy( subRoot.get( "longitude" ),
subRoot.get( "latitude" ),
subRoot.get( "updateTime" ) );
countQuery.where( root.get( "id" ).in( subQuery ) );
Long count = entityManager.createQuery( countQuery ).getSingleResult();
这有效地生成以下SQL:
SELECT COUNT( p0.id ) FROM Position p0
WHERE p0.id IN (
SELECT MIN( p1.id )
FROM Position p1
GROUP BY p1.longitude, p1.latitude, p1.updateTime )
在我有3行且其中2行具有相同的经度,纬度和更新时间元组的情况下,查询将返回2的结果。
确保在[Longtitude,Latitude,UpdateTime]上保持良好的索引,以便您可以利用更快的GROUP BY执行。 PK已经被b-tree索引,因此其他操作的COUNT / MIN应该很容易被该索引解释。