如何使用 queryDSL 处理 mysql json 列?

how to treat mysql json column with queryDSL?

我想知道如何处理 mysql json 列。

product table:
...
    category_ids                     json                         null,
...

category_ids 例如)[19, 102, 108]

如果我想搜索包含类别 ID 102 的产品列表 我如何使用 queryDSL 进行查询?

我尝试使用 JsonNode 类型,但它不起作用。

根据标签,我假设您使用 querydsl-jpa 创建 JPQL(或 Hibernates HQL)查询。这一点很重要,因为为了让 JSON 类型与 Querydsl 一起工作,它们不仅必须与 Querydsl 和 MySQL 一起工作,而且最重要的是与查询语言本身一起工作,在这种情况下来自休眠。

我假设您已经设法将 json 列正确映射到 Jacksons JsonNode 类型。如果不是,那么请务必注意,对于任何非标准类型,自定义类型都需要在 Hibernate 中注册。您可以编写自己的自定义类型,也可以使用现有的可用实现。我强烈推荐 Vlad 开发的 hibernate-types 库(其他一些人的贡献,包括我自己)。他对这个 Whosebug 问题的回答很好地解释了如何将 hibernate-types 集成到您的项目中:

The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:

   <dependency>
       <groupId>com.vladmihalcea</groupId>
       <artifactId>hibernate-types-52</artifactId>
       <version>${hibernate-types.version}</version>
   </dependency>

Now, you need to declare the JsonType on either class level or in a package-info.java package-level descriptor, like this:

   @TypeDef(name = "json", typeClass = JsonType.class)

And, the entity mapping will look like this:

   @Type(type = "json")
   @Column(columnDefinition = "jsonb")
   private Location location;

现在您将可以在查询中的简单操作中使用JSON 属性。允许的操作基本上包括基本比较(=!=IS (NOT) NULL)。其他操作不可用,因为这些仅在 MySQL 中定义,而不是在 JPQL 或 HQL 查询语言中定义。

您想检查 JSON 数组是否包含特定元素。我假设 category_ids 代表一个 JSON 数组。那么这意味着您想要生成的 MySQL 片段可能类似于 category_ids ? :categoryId.

我想指出的是,这仅适用于存储为字符串的 ID,而不是 JSON 数组中的数字。您可能希望将 category_ids 映射为 bigint[]。与 JSON 类型一样,Hibernate-Types 库也支持数组类型,因此答案对数组同样适用。

但是,JSON_CONTAINS(category_ids, :categoryId) 无效 HQL/JPQL,因为该查询语言中不存在该函数。如果我们想在 HQL/JPQL 中呈现 JSON_CONTAINS(category_ids, :categoryId),我们需要定义一个将 JSON_CONTAINS(category_ids, :categoryId) 呈现为 SQL.

的自定义函数

有两种方法可以在 Hibernate 中声明自定义函数。

  1. 扩展您正在使用的方言并在那里声明函数。 中详细描述了这种方法。
  2. 在 bootstrap 期间使用 MetadataBuilderInitializer SPI 注册自定义函数。 .
  3. 中描述了这种方法

使用 MetadataBuilderInitializer 方法,代码最终将如下所示:

public class JSONMetadataBuilderInitializer
        implements MetadataBuilderInitializer {
    @Override
    public void contribute(MetadataBuilder metadataBuilder,
            StandardServiceRegistry serviceRegistry) {
        metadataBuilder.applySqlFunction("json_contains_key",
            new SQLFunctionTemplate(BooleanType.INSTANCE,
                "JSON_CONTAINS(?1, ?2, '$'"));
    }
}

最后,您得到了 JsonNode 属性 的 Hibernate 类型,以及检查键是否存在的自定义函数。 JPQL/HQL中的key可以这样使用:

SELECT product FROM Product product WHERE json_contains_key(product.categories, '1234') = true;

这将产生以下 SQL:

SELECT * FROM product WHERE JSON_CONTAINS(categories, '1234', '$') = true;

但是,您想通过Querydsl 构造此JPQL/HQL 查询,这意味着还剩下几个步骤。我们需要为创建的函数构造新的表达式类型。基本上有两个选项:

  1. 创建一个新的 Operator 实现,并在扩展 JPQLTemplates.
  2. 中为那个 Operator 注册一个 Template
  3. 创建一个 TemplateExpression(字面意思是 JPQL 的片段)。

第二种方法更简单,我将在此处使用的方法也是如此。使用 TemplateExpression,您最终会在 Querydsl 中使用以下代码生成上述 JPQL 查询:

queryFactory.select(QProduct.product)
    .from(QProduct.product)
    .where(Expressions.booleanTemplate(
        "json_contains_key({0}, {1})",
        QProduct.product.categories,
        Expressions.constants("1234").isTrue())
    .fetch();

但是,这并不像您在 Querydsl 中习惯的那样流畅。可以使用自定义表达式类型扩展 Querydsl,并在此假设引入 JsonExpression。也可以扩展 querydsl-apt 以在静态元模型(Q-类)中自动生成此表达式类型的路径。我专门为此目的构建了扩展库 hibernate-types-querydsl-apt。它为 Hibernate-Types 项目中的自定义类型(例如范围、数组、json、hstore、间隔)添加了各种自定义表达式类型。它还会自动为主要操作注册自定义函数。使用我的扩展,数组的用例将很简单:

@Entity
@TypeDefs({
        @TypeDef(name = "int-array", typeClass = IntArrayType.class, defaultForType = int[].class)
})
public class ArrayEntity {
    @Id
    Long id;

    @Type(type = "int-array")
    @Column(name = "sensor_values", columnDefinition = "integer[]")
    int[] sensorValues;
}
List<Tuple> fetch = new JPAQuery<>(entityManager, ExtendedHQLTemplates.DEFAULT)
    .from(arrayEntity)
    .select(arrayEntity)
    .where(arrayEntity.sensorValues.contains(123))
    .fetch();

更多示例位于:https://github.com/jwgmeligmeyling/hibernate-types-querydsl-apt/blob/master/querydsl-ext-testsuite/src/test/java/com/pallasathenagroup/querydsl/ArrayEntityPathTest.java .