如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?
Posted
技术标签:
【中文标题】如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?【英文标题】:How to select data in spring boot using jpa based on where condition of jsonb column of postgres(without native query)? 【发布时间】:2019-07-27 22:44:39 【问题描述】:我有带有 jsonb 字段的 Postgres 数据库。我在 jpa 中使用了条件生成器的谓词列表作为条件。现在,我想获取存储在基于多个 where 原因以及 JSON 的数据库中的 JSON 数据。怎么可能呢?
Database table
private List<Predicate> whereClause(CriteriaBuilder criteriaBuilder, Root<KafkaLog> kafkaLog,
KafkaLogSearchDto search)
List<Predicate> predicates = new ArrayList<>();
if (search.getTopic() != null && !search.getTopic().isEmpty())
Expression<String> literal =
criteriaBuilder.literal("%" + search.getTopic().toUpperCase() + "%");
predicates.add(criteriaBuilder.like(criteriaBuilder.upper(kafkaLog.get("topic")), literal));
if (search.getOffset() != null)
predicates.add(criteriaBuilder.equal(kafkaLog.get("offset"), search.getOffset()));
if (search.getResourceId() != null)
predicates.add(criteriaBuilder.equal(kafkaLog.get("invoiceId"), search.getResourceId()));
if (search.getPartition() != null)
predicates.add(criteriaBuilder.equal(kafkaLog.get("partition"), search.getPartition()));
if (search.getStatus() != null)
predicates.add(criteriaBuilder.equal(kafkaLog.get("status"), search.getStatus()));
if (search.getCreatedAtFrom() != null && search.getCreatedAtTo() != null)
predicates.add(criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(kafkaLog.get("createdAt").as(Date.class),
search.getCreatedAtFrom()),
criteriaBuilder.lessThanOrEqualTo(kafkaLog.get("createdAt").as(Date.class),
search.getCreatedAtTo())));
if (search.getPayload() != null && !search.getPayload().isEmpty())
Expression<String> literal =
criteriaBuilder.literal("%" + search.getPayload().toUpperCase() + "%");
return predicates;
private CriteriaQuery<KafkaLog> getKafkaBySearchCriteria(KafkaLogSearchDto search, String orderBy,
int sortingDirection)
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<KafkaLog> criteriaQuery = criteriaBuilder.createQuery(KafkaLog.class);
Root<KafkaLog> kafkaLog = criteriaQuery.from(KafkaLog.class);
List<Predicate> predicates = whereClause(criteriaBuilder, kafkaLog, search);
criteriaQuery.where(predicates.toArray(new Predicate[] ));
if (orderBy == null || orderBy.isEmpty())
orderBy = "topic";
Expression<?> orderColumn = kafkaLog.get(orderBy);
if (orderColumn != null)
if (sortingDirection == 0)
criteriaQuery.orderBy(criteriaBuilder.asc(orderColumn));
else if (sortingDirection == 1)
criteriaQuery.orderBy(criteriaBuilder.desc(orderColumn));
return criteriaQuery;
【问题讨论】:
【参考方案1】:显然,对 JDBC/数据库级别的自定义数据类型支持并不是 JPA 本身的一部分。对于仅读取 JSON,您可能可以使用到 String
的实体映射来完成它(如果 JPA 实现允许这样做)。
但是,您有一些选择:
-
如果您需要坚持使用 JPA,您可以实现一个转换器来为您处理 JSON(本文中的通用或更具体 - 随您的喜好):Using JPA with PostgreSQL JSON
如果可以的话,放弃 JPA 并直接使用 JDBC/SQL 来实现,这可以让您充分发挥 PostgreSQL 的潜力:how to store PostgreSQL jsonb using SpringBoot + JPA?
在读取 JSONB 字段时,您可以让数据库使用显式类型转换来转换为字符串(因为 JDBC 中还没有直接的 JSON 支持),例如:
SELECT payload::text FROM my_table WHERE ...
...或者对 JSON 字段本身进行一些花哨的过滤,或者只返回一部分 JSON 数据,aso。
【讨论】:
感谢 Ancoron 的回复。正如您所建议的那样,我已经尝试将其转换为字符串,但是对于 Rest 调用来说太耗时了。在数据库中搜索大约需要 45-50 秒。因此,我在这种情况下变得束手无策。此外,我不能对特定的有效负载列使用 nativequery。 好的,那么我们需要查看 JPA 实现从条件查询中生成了哪个实际 SQL,然后您应该执行EXPLAIN (ANALYZE, BUFFERS) <your-sql-here>
并编辑您的问题(查询太慢?)或为此创建一个新问题。在我看来,你已经让它在功能上正常工作了,只是它太慢了,对吧?【参考方案2】:
这是使用 like 子句搜索 jsonb 的示例谓词:
predicate.getExpressions().add(cb.and(cb.like(cb.function("jsonb_extract_path_text", String.class, root.get("tags"), cb.literal(this.key))), "%"+ this.value + "%" )));
【讨论】:
以上是关于如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?的主要内容,如果未能解决你的问题,请参考以下文章