You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL 14使用json_agg执行JPA原生查询遇JDBC类型1111映射错误

解决PostgreSQL 14下JPA原生查询json_agg引发的MappingException问题

问题核心是Hibernate默认未将PostgreSQL的JSON类型(JDBC类型1111)映射到JsonNode,以下是几种可行解决方案:

方案1:扩展PostgreSQL方言,注册JSON类型映射

自定义方言补充JDBC类型1111到JsonNode的映射:

import org.hibernate.dialect.PostgreSQL14Dialect;
import com.fasterxml.jackson.databind.JsonNode;

public class CustomPostgreSQLDialect extends PostgreSQL14Dialect {
    public CustomPostgreSQLDialect() {
        super();
        registerHibernateType(1111, JsonNode.class.getName());
    }
}

在配置文件中指定自定义方言:

spring.jpa.properties.hibernate.dialect=com.yourpackage.CustomPostgreSQLDialect

方案2:使用自定义AttributeConverter转换JSON字符串到JsonNode

创建转换器自动处理JSON字符串与JsonNode的互转,同时修改查询将json_agg结果转为文本:

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

@Converter(autoApply = true)
public class JsonNodeConverter implements AttributeConverter<JsonNode, String> {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(JsonNode jsonNode) {
        try {
            return objectMapper.writeValueAsString(jsonNode);
        } catch (IOException e) {
            throw new RuntimeException("JsonNode转字符串失败", e);
        }
    }

    @Override
    public JsonNode convertToEntityAttribute(String s) {
        try {
            return objectMapper.readTree(s);
        } catch (IOException e) {
            throw new RuntimeException("字符串转JsonNode失败", e);
        }
    }
}

修改原生查询,将json_agg结果转为text:

SELECT id, name, json_agg(channels)::text AS channels FROM ... GROUP BY id, name

方案3:通过@SqlResultSetMapping显式映射类型

定义结果集映射,明确指定channels字段的类型:

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
import com.fasterxml.jackson.databind.JsonNode;

@Entity
@SqlResultSetMapping(
    name = "NotificationListDtoMapping",
    classes = @ConstructorResult(
        targetClass = NotificationListDto.class,
        columns = {
            @ColumnResult(name = "id", type = Long.class),
            @ColumnResult(name = "name", type = String.class),
            @ColumnResult(name = "channels", type = JsonNode.class)
        }
    )
)
public class DummyMappingEntity {
    @Id
    private Long id;
}

执行查询时指定该映射:

Query query = entityManager.createNativeQuery(
    "SELECT id, name, json_agg(channels) AS channels FROM ... GROUP BY id, name",
    "NotificationListDtoMapping"
);
List<NotificationListDto> result = query.getResultList();

方案4:使用Hibernate Types库简化配置

引入Hibernate Types库直接支持JSON类型映射:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-55</artifactId> <!-- 匹配你的Hibernate版本 -->
    <version>2.19.0</version>
</dependency>

在DTO字段上添加@Type注解:

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

public interface NotificationListDto {
    Long getId();
    String getName();
    @Type(JsonNodeBinaryType.class)
    JsonNode getChannels();
}

内容的提问来源于stack exchange,提问作者Rajanboy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:08:28