Spring Boot+PostgreSQL JSON字段自定义投影映射失败
问题原因
在JPQL投影查询中,Hibernate未自动应用实体字段上@Type(JsonType.class)的类型转换逻辑,导致查询返回的config字段是原始JDBC JSON对象(如PostgreSQL的PGobject),而非映射后的Config类型,与FooProjection的构造器参数类型不匹配,触发实例化错误。
解决方案
方案1:添加兼容Object类型的构造器
修改FooProjection,新增一个接受Long和Object类型参数的构造器,手动将JDBC JSON对象转换为Config类型:
@Getter @Builder @RequiredArgsConstructor public class FooProjection { private final Long id; private final Config config; // 新增构造器,处理JDBC返回的JSON对象 public FooProjection(Long id, Object configObj) { this.id = id; if (configObj instanceof PGobject pgObj) { try { this.config = new ObjectMapper().readValue(pgObj.getValue(), Config.class); } catch (Exception e) { throw new RuntimeException("Failed to parse Config from JSON", e); } } else { // 兼容已转换为Config类型的情况 this.config = (Config) configObj; } } }
需导入PostgreSQL驱动的
org.postgresql.util.PGobject类,确保项目依赖PostgreSQL JDBC驱动。
方案2:改用JPA AttributeConverter替代Hibernate JsonType
用JPA标准的@Convert替换实体上的@Type(JsonType.class),让Hibernate在JPQL查询中自动处理类型转换:
- 创建自定义转换器:
import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; import com.fasterxml.jackson.databind.ObjectMapper; @Converter(autoApply = true) public class ConfigConverter implements AttributeConverter<Config, String> { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(Config config) { try { return objectMapper.writeValueAsString(config); } catch (Exception e) { throw new RuntimeException("Failed to serialize Config to JSON", e); } } @Override public Config convertToEntityAttribute(String dbJson) { try { return dbJson == null ? null : objectMapper.readValue(dbJson, Config.class); } catch (Exception e) { throw new RuntimeException("Failed to deserialize JSON to Config", e); } } }
- 修改Foo实体的config字段:
// 替换原有的@Type(JsonType.class) @Convert(converter = ConfigConverter.class) private Config config;
修改后无需调整原查询语句,JPQL会自动将数据库JSON字符串转换为Config对象,正常实例化FooProjection。
方案3:在JPQL中显式转换类型
在查询语句中使用CAST显式指定类型转换,需确保Hibernate已注册Config对应的JSON类型:
@Query( """ SELECT new path.to.package.FooProjection( f.id, CAST(f.config AS path.to.package.Config) ) FROM Foo f WHERE ... """) Optional<FooProjection> fetchMinimalFoo(Long fooId);
若使用vladmihalcea的
JsonType,需在Hibernate配置中注册该类型为可转换的自定义类型。
内容的提问来源于stack exchange,提问作者Hayi
相关产品推荐
相关产品推荐

