给定一个只包含主键和DATETIME文件'create_date'的表,我想用JPA criteria api写一个查询,从给定的日期开始计算在不同时间间隔内创建的行数(例如:2小时,12小时,3天)。intervalStart
在下面的代码中).在未来将增加额外的列,用户应该能够通过这些列过滤结果--因此需要建立动态查询。将来会添加更多的列,用户应该能够通过这些列来过滤结果--因此需要动态地建立查询。查询应该在PostgreSQL 9+上运行。
实体类。
@Entity(name = "foo")
public class Foo {
@Id
@Column(name = "id", nullable = false)
private long id;
@Column(name = "create_date", columnDefinition = "TIMESTAMP")
private LocalDateTime createDate;
}
建立查询的代码(12小时间隔)。
public class FooQueryFactory {
private EntityManager entityManager;
public CriteriaQuery<Tuple> buildQuery(LocalDateTime intervalStart) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<Foo> foo = query.from(Foo.class);
Expression<Integer> dateRange = getDateRangeExpression(criteriaBuilder, foo, intervalStart);
return query.multiselect(dateRange.alias("date_range"), criteriaBuilder.count(foo).alias("count"))
.groupBy(dateRange)
.orderBy(criteriaBuilder.asc(dateRange));
}
private Expression<Integer> getDateRangeExpression(CriteriaBuilder criteriaBuilder, Root<Foo> foo,
LocalDateTime intervalStart) {
long intervalLengthInSeconds = 12 * 60 * 60;
return criteriaBuilder.function("floor", Integer.class,
criteriaBuilder.quot(secondsSinceIntervalStart(criteriaBuilder, foo, intervalStart),
criteriaBuilder.literal(intervalLengthInSeconds)));
}
private Expression<Long> secondsSinceIntervalStart(CriteriaBuilder criteriaBuilder, Root<Foo> foo,
LocalDateTime from) {
Expression<Long> createDateAsEpoch =
criteriaBuilder.function("date_part", Long.class, criteriaBuilder.literal("epoch"), foo.get("createDate"));
return criteriaBuilder.diff(getEpoch(from), createDateAsEpoch);
}
private long getEpoch(LocalDateTime dateTime) {
return dateTime.atZone(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))).toEpochSecond();
}
}
遗憾的是,执行查询时出现了异常。
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1535)
at org.hibernate.query.Query.getResultList(Query.java:165)
at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:76)
(...)
Caused by: org.postgresql.util.PSQLException: ERROR: column "foo0_.create_date" must appear in the GROUP BY clause or be used in an aggregate function
Position: 407
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2533)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2268)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:313)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:448)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:369)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:159)
at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:109)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
... 135 more
然而,如果我把创建的SQL复制到数据库控制台,它就会成功执行并产生预期的结果。
19:15:24.369 DEBUG [main] o.h.SQL - select floor((1497106800-date_part('epoch', foo0_.create_date))/7200) as col_0_0_, count(foo0_.id) as col_2_0_ from foo foo0_ group by floor((1497106800-date_part('epoch', foo0_.create_date))/7200) order by floor((1497106800-date_part(?, foo0_.create_date))/7200) asc
19:15:24.370 TRACE [main] o.h.t.d.s.BasicBinder - binding parameter [1] as [VARCHAR] - [epoch]
19:15:24.391 WARN [main] o.h.e.j.s.SqlExceptionHelper - SQL Error: 0, SQLState: 42803
19:15:24.392 ERROR [main] o.h.e.j.s.SqlExceptionHelper - ERROR: column "foo0_.create_date" must appear in the GROUP BY clause or be used in an aggregate function
Position: 407