Quarkus Panache+PostgreSQL:JSONB存储转义问题求助
问题场景
我在开发Quarkus Panache结合PostgreSQL的项目时,尝试将JSON数据以JSONB格式存储,但插入数据时JSON会被自动转义,导致数据库中存储的是带转义符的字符串,无法正常执行PostgreSQL的JSON查询。
当前插入代码
JsonObject jsonObject = Json.createObjectBuilder() .add("name", "John") .add("age", 30) .add("city", "New York") .build(); String jsonString = jsonObject.toString(); // Convert JsonObject to JSON String return update("update OfferEntity o set o.items = ?1", jsonString).onItem().transform(ignored -> offer);
数据库实际存储结果
"{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}"
期望存储格式
{"name":"John","age":30,"city":"New York"}
原实体类代码
@Entity @Table(name = "offering") @Cacheable @WithSession public class OfferEntity extends PanacheEntityBase { @JdbcTypeCode(SqlTypes.JSON) @Column(name = "items", columnDefinition = "jsonb", insertable = false, updatable = false) private String items; public String getItems() { return items; } public void setItems(String items) { this.items = items; } }
已尝试的无效方案
我试过两种修改实体类的方式,但结果还是一样,数据库中依然是转义后的格式:
方案1:使用自定义实体类
自定义Item类:
public class Item implements Serializable { private String name; private int age; private String city;// 已添加getter和setter }
修改OfferEntity的items字段:
@JdbcTypeCode(SqlTypes.JSON) @Column(name = "items", columnDefinition = "jsonb", insertable = false, updatable = false) private Item items; public Item getItems() { return items; } public void setItems(Item items) { this.items = items; }
方案2:使用Map类型
修改OfferEntity的items字段:
@JdbcTypeCode(SqlTypes.JSON) @Column(name = "items", columnDefinition = "jsonb", insertable = false, updatable = false) private Map<String, String> items; public Map<String, String> getItems() { return items; } public void setItems(Map<String, String> items) { this.items = items; }
当前使用Hibernate版本为6.2.7 final,这会不会是Hibernate的Bug?
解决方案
1. 移除实体类字段的insertable = false, updatable = false配置
你当前的@Column注解里设置了insertable = false, updatable = false,这会让Hibernate跳过对该字段的自动持久化处理,导致你手动用JPQL更新时,直接把字符串当作普通文本插入,从而被转义。
修改后的字段注解:
@JdbcTypeCode(SqlTypes.JSON) @Column(name = "items", columnDefinition = "jsonb") private String items;
2. 避免手动转JSON字符串,直接使用实体类赋值
不要手动把JsonObject转成字符串再通过JPQL更新,而是直接给实体类的items字段赋值,让Hibernate自动处理JSON序列化:
JsonObject jsonObject = Json.createObjectBuilder() .add("name", "John") .add("age", 30) .add("city", "New York") .build(); offer.setItems(jsonObject.toString()); // 直接赋值给实体字段 offer.persistOrUpdate(); // 使用Panache的持久化方法 return offer;
3. 若使用自定义实体类/Map类型
确保移除insertable = false, updatable = false,然后直接赋值对象/Map,Hibernate会自动序列化为JSONB:
Item item = new Item(); item.setName("John"); item.setAge(30); item.setCity("New York"); offer.setItems(item); offer.persistOrUpdate(); return offer;
4. 若必须使用JPQL更新
如果一定要用JPQL的update语句,需要让参数以JSON类型传递,而不是字符串:
JsonObject jsonObject = Json.createObjectBuilder() .add("name", "John") .add("age", 30) .add("city", "New York") .build(); // 使用EntityManager指定参数为JSON类型 return getEntityManager() .createQuery("update OfferEntity o set o.items = ?1") .setParameter(1, jsonObject, JsonType.INSTANCE) .executeUpdate();
关键原因
问题核心是insertable = false, updatable = false导致Hibernate不识别该字段为JSON类型,手动插入字符串时被当作普通文本处理,加上转义符。移除这个配置后,Hibernate会根据@JdbcTypeCode(SqlTypes.JSON)和columnDefinition = "jsonb"自动处理JSON的序列化和反序列化,避免转义。
内容的提问来源于stack exchange,提问作者Theodosis

