如何在原生查询中通过Projection获取JSON类型列?
如何通过Projection获取JSON列(解决Hibernate方言映射错误)
当使用原生查询关联其他表并通过接口Projection查询特定列时,Hibernate无法正确映射JSONB列,报错没有JDBC类型1111的方言映射。全实体查询时正常,无JSON列场景下Projection也能正常工作。
你的实体类和Projection定义如下:
实体类
@Entity public class EntityWithJsonColumn // ... 其他字段 @Type(type = "jsonb") @Column(columnDefinition = "jsonb") private Map<String,Object> additionalInfo; // ...
Projection接口
public interface DataProjection { // ... 其他方法 @Value("#{target.id}") Long getID(); @Value("#{target.additionalInfo}") Map<String,Object> getAdditionalInfo(); // ... }
解决方案
方法1:SQL转换+Projection默认方法处理
在原生SQL中将JSON列转换为字符串,再在Projection中通过默认方法将字符串反序列化为Map:
- 修改原生查询,将JSON列转为文本:
SELECT e.id, e.additional_info::text AS additionalInfo FROM entity_with_json_column e JOIN other_table ot ON ... -- 你的关联条件
- 更新Projection接口,新增字符串接收方法和默认转换方法:
public interface DataProjection { Long getID(); // 先接收转换后的字符串 String getAdditionalInfo(); // 默认方法将字符串转为Map default Map<String, Object> parseAdditionalInfo() { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(getAdditionalInfo(), new TypeReference<Map<String, Object>>() {}); } catch (JsonProcessingException e) { throw new RuntimeException("解析JSON列失败", e); } } }
方法2:使用@SqlResultSetMapping指定类型映射
通过定义结果集映射,显式告诉Hibernate如何处理JSON列:
- 创建DTO类作为投影目标(替代接口Projection):
public class DataDto { private Long id; private Map<String, Object> additionalInfo; public DataDto(Long id, Map<String, Object> additionalInfo) { this.id = id; this.additionalInfo = additionalInfo; } // getter方法 public Long getId() { return id; } public Map<String, Object> getAdditionalInfo() { return additionalInfo; } }
- 在实体类上添加结果集映射:
@Entity @SqlResultSetMapping( name = "DataDtoMapping", classes = @ConstructorResult( targetClass = DataDto.class, columns = { @ColumnResult(name = "id", type = Long.class), @ColumnResult(name = "additionalInfo", type = JsonBinaryType.class) } ) ) public class EntityWithJsonColumn { // ... 现有字段和注解 }
- 在Repository中使用该映射执行原生查询:
@Repository public interface EntityWithJsonColumnRepository extends JpaRepository<EntityWithJsonColumn, Long> { @Query( value = "SELECT e.id, e.additional_info AS additionalInfo FROM entity_with_json_column e JOIN other_table ot ON ...", nativeQuery = true ) @SqlResultSetMapping(name = "DataDtoMapping") List<DataDto> findProjectedData(); }
方法3:扩展Hibernate方言全局映射
通过自定义方言,让Hibernate自动识别JDBC类型1111(对应JSON)并映射为JSON类型:
- 创建自定义PostgreSQL方言(如果用其他数据库,替换对应方言类):
public class CustomPostgresDialect extends PostgreSQL10Dialect { public CustomPostgresDialect() { super(); // 将JDBC类型1111映射为Hibernate的JsonBinaryType registerHibernateType(Types.OTHER, JsonBinaryType.class.getName()); } }
- 在配置文件中指定自定义方言:
# application.properties spring.jpa.properties.hibernate.dialect=com.yourpackage.CustomPostgresDialect
配置完成后,原有的接口Projection无需修改,原生查询即可正确映射JSON列到Map。
内容的提问来源于stack exchange,提问作者Aleksandr Stepanov
相关产品推荐
相关产品推荐

