【发布时间】:2020-09-11 00:58:08
【问题描述】:
我正在尝试使用 JPA 的 Criteria API 按日期间隔对实体进行分组。我使用这种查询实体的方式,因为这是服务 API 请求的服务的一部分,API 请求可能要求任何实体的任何字段,包括排序、过滤、分组和聚合。除了按日期字段分组外,一切正常。我的底层 DBMS 是 PostgreSQL。
举个简单的例子,这是我的实体类:
@Entity
@Table(name = "receipts")
public class DbReceipt {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Date sellDate;
// Many other fields
}
这个例子讨论了对我的“月”间隔进行分组(因此按年+月分组),但最后我正在寻找一种可以让我按任何间隔分组的解决方案,例如“年”、“日”或“分钟”。
我想要实现的是以下查询,但使用 Criteria API:
SELECT TO_CHAR(sell_date, 'YYYY-MM') AS alias1 FROM receipts GROUP BY alias1;
我的尝试是这样的:
@Service
public class ReceiptServiceImpl extends ReceiptService {
@Autowired
private EntityManager em;
@Override
public void test() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<?> root = query.from(DbReceipt.class);
Expression<?> expr = cb.function("to_char", String.class, root.get("sellDate"), cb.literal("YYYY-MM"));
query.groupBy(expr);
query.multiselect(expr);
TypedQuery<Object[]> typedQuery = em.createQuery(query);
List<Object[]> resultList = typedQuery.getResultList();
}
}
我使用to_char 函数而不是MONTH 和类似函数的原因是我需要不将2019-05 和2020-05 之类的实体组合在一起。我还将这个例子缩小到只有年和月以保持简短,但目标是按任何日期间隔分组。
上面的代码创建了以下导致错误的查询(启用 SQL 日志记录):
Hibernate: select to_char(dbreceipt0_.sell_date, ?) as col_0_0_ from receipts dbreceipt0_ group by to_char(dbreceipt0_.sell_date, ?)
24-05-2020 12:16:30.071 [http-nio-1234-exec-5] WARN o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - SQL Error: 0, SQLState: 42803
24-05-2020 12:16:30.071 [http-nio-1234-exec-5] ERROR o.h.e.jdbc.spi.SqlExceptionHelper.logExceptions - ERROR: column "dbreceipt0_.sell_date" must appear in the GROUP BY clause or be used in an aggregate function
Position: 16
这对我来说是由于整个表达式被放入查询的“分组依据”部分,而不仅仅是一个别名。现在,我尝试为表达式分配一个别名(它返回 Selection<T> 并且 groupBy 接受表达式,因此我只能在 multiselect 中真正使用它),但这并不影响查询的执行方式 -没有任何改变。
如何使用 Criteria API 实现如上所述的按年和月分组?除了使用to_char之外,也许还有其他方法?也许有一种方法可以为 groupBy 方法提供别名,这会导致它按别名而不是整个表达式进行分组?
【问题讨论】:
标签: postgresql hibernate spring-boot jpa criteria-api