json 字段未使用自定义投影正确映射

问题描述 投票:0回答:1

我正在使用 postgresql 和 spring boot,并且我有一个像这样定义的 json 列

ALTER TABLE core."foo"
    ADD COLUMN if not exists config json;

Foo
实体的映射如下

import com.vladmihalcea.hibernate.type.json.JsonType;
import org.hibernate.annotations.Type;

  ...
  @Type(JsonType.class)
  private Config config;

Foo
实体包含很多列,因此我使用自定义 dto 来执行一些优化的选择

  @Query(
  """
    SELECT new path.to.package.FooProjection(
      f.id,
      f.config
    )
    FROM Foo f
    WHERE ...
  """)
  Optional<FooProjection> fetchMinimalFoo(Long fooId);

FooProjection
是经典的 pojo,
config
字段映射如下

@Getter
@Builder
@RequiredArgsConstructor
public class FooProjection {

  private final Long id;
  private final Config config;

问题是执行查询时出现此错误

java.lang.IllegalStateException: Cannot instantiate class 'path.to.package.FooProjection' 
(it has no constructor with signature [java.lang.Long, java.lang.Object], and not every argument has an alias)

它将 json 字段视为一个对象!!

postgresql spring-boot hibernate jpa spring-data-jpa
1个回答
0
投票

在阅读了 Thorben Janssen 的“实体或 DTO – 何时应该使用哪个投影?”和“为什么、何时以及如何在 JPA 和 Hibernate 中使用 DTO 投影”后,很明显 Hibernate 在DTO 投影,并且构造函数必须与查询返回的确切类型匹配。

由于 Hibernate 将 JSON 字段视为通用

Object
,或者可能是其他默认类型,如
String
,具体取决于您的 Hibernate 方言,因此您需要确保您的
FooProjection
构造函数可以处理此问题。

更新

FooProjection
以接受 JSON 字段作为 Hibernate 将其转换为的类型。
如果是
String
(常见情况),您会看到类似:

@Getter
@Builder
@RequiredArgsConstructor
public class FooProjection {

  private final Long id;
  private final String configJson; // Changed to String assuming JSON is returned as a String

  // Constructor matching the query projection
  public FooProjection(Long id, String configJson) {
      this.id = id;
      this.config = Config.createFromJson(configJson); // Assuming you have a method to handle JSON parsing
  }
}

如果 Hibernate 没有自然地将 JSON 转换为字符串,您可能需要在查询中显式转换或转换它。这取决于您的特定数据库方言。对于 PostgreSQL,您可以修改查询以将 JSON 转换为文本: @Query( """ SELECT new path.to.package.FooProjection( f.id, f.config::jsonb#>>'{}' // Casts to JSONB and fetches the whole JSON object as text ) FROM Foo f WHERE f.id = :fooId """) Optional<FooProjection> fetchMinimalFoo(@Param("fooId") Long fooId);

假设 
config

可以完全表示为字符串。

如果 
config
包含嵌套对象,并且您正在尝试检索特定值,则需要相应地调整
#>>
运算符中的路径。
确保您的 

Config

类或相关实用程序中有一个方法可以将 JSON 字符串转换回您的

Config
对象。
JSON 到 
Config
对象的转换应该发生在代码中的正确位置:

直接在 DTO 的构造函数中,如我之前所述,或者
  • 在服务方法中,您在检索后但在业务逻辑或表示层中使用数据之前处理数据。
  • import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.JsonProcessingException; public class JsonUtil { private static final ObjectMapper objectMapper = new ObjectMapper(); public static Config createFromJson(String json) { try { return objectMapper.readValue(json, Config.class); } catch (JsonProcessingException e) { // Log the error or throw a custom exception as per your error handling policy System.err.println("Error parsing JSON to Config: " + e.getMessage()); return null; // or throw, depending on how you want to handle parse errors } } }
FooProjection

DTO 必须正确使用此方法:如果

config
中的
FooProjection
字段是
Config
类型,则需要修改构造函数来解析 JSON 字符串:
@Getter
@Builder
@RequiredArgsConstructor
public class FooProjection {

    private final Long id;
    private Config config;  // Not final, to allow setting after parsing

    public FooProjection(Long id, String configJson) {
        this.id = id;
        this.config = JsonUtil.createFromJson(configJson);  // Convert JSON string to Config
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.