如何在 JPA 中使用 Postgres JSONB 数据类型?
Posted
技术标签:
【中文标题】如何在 JPA 中使用 Postgres JSONB 数据类型?【英文标题】:How to use Postgres JSONB datatype with JPA? 【发布时间】:2017-02-11 08:28:22 【问题描述】:我没有找到使用 JPA (EclipseLink) 从 PostgreSQL 映射 JSON 和 JSONB 数据类型的方法。有人在 JPA 中使用这种数据类型,可以给我一些工作示例吗?
【问题讨论】:
SO上有一个例子,让我看看能不能找到... 【参考方案1】:编辑:我现在看到这几乎与 Hibernate
相关。但也许你可以为 EclipseLink
找到类似的东西。
我将添加我所拥有的作为答案,它源自另一个 SO 答案,但无论如何。这会将 jsonb
映射到 Google gson
的 JsonObject
,但如果需要,您可以将其更改为其他内容。要更改为其他内容,请更改 nullSafeGet
、nullSafeSet
和 deepCopy
方法。
public class JsonbType implements UserType
@Override
public int[] sqlTypes()
return new int[] Types.JAVA_OBJECT ;
@Override
public Class<JsonObject> returnedClass()
return JsonObject.class;
@Override
public boolean equals(final Object x, final Object y)
if (x == y)
return true;
if (x == null || y == null)
return false;
return x.equals(y);
@Override
public int hashCode(final Object x)
if (x == null)
return 0;
return x.hashCode();
@Nullable
@Override
public Object nullSafeGet(final ResultSet rs,
final String[] names,
final SessionImplementor session,
final Object owner) throws SQLException
final String json = rs.getString(names[0]);
if (json == null)
return null;
final JsonParser jsonParser = new JsonParser();
return jsonParser.parse(json).getAsJsonObject();
@Override
public void nullSafeSet(final PreparedStatement st,
final Object value,
final int index,
final SessionImplementor session) throws SQLException
if (value == null)
st.setNull(index, Types.OTHER);
return;
st.setObject(index, value.toString(), Types.OTHER);
@Nullable
@Override
public Object deepCopy(@Nullable final Object value)
if (value == null)
return null;
final JsonParser jsonParser = new JsonParser();
return jsonParser.parse(value.toString()).getAsJsonObject();
@Override
public boolean isMutable()
return true;
@Override
public Serializable disassemble(final Object value)
final Object deepCopy = deepCopy(value);
if (!(deepCopy instanceof Serializable))
throw new SerializationException(
String.format("deepCopy of %s is not serializable", value), null);
return (Serializable) deepCopy;
@Nullable
@Override
public Object assemble(final Serializable cached, final Object owner)
return deepCopy(cached);
@Nullable
@Override
public Object replace(final Object original, final Object target, final Object owner)
return deepCopy(original);
要使用它,请执行以下操作:
public class SomeEntity
@Column(name = "jsonobject")
@Type(type = "com.myapp.JsonbType")
private JsonObject jsonObject;
另外,你需要设置你的方言来表示JAVA_OBJECT
= jsonb
:
registerColumnType(Types.JAVA_OBJECT, "jsonb");
【讨论】:
【参考方案2】:我想我找到了与 Hibernate 的 EclipseLink 的 UserType 的类比。
http://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/annotations_ref.htm#CHDEHJEB
您必须创建一个实现org.eclipse.persistence.mappings.converters.Converter
并为您进行转换的类,然后在您使用该类型的每个字段上使用@Convert
注释。
【讨论】:
【参考方案3】:所有答案都帮助我找到了为 JPA 而不是专门为 EclipseLink 或 Hibernate 做好准备的最终解决方案。
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import javax.json.Json;
import javax.json.JsonObject;
import javax.persistence.Converter;
import org.postgresql.util.PGobject;
@Converter(autoApply = true)
public class JsonConverter implements javax.persistence.AttributeConverter<JsonObject, Object>
private static final long serialVersionUID = 1L;
private static ObjectMapper mapper = new ObjectMapper();
@Override
public Object convertToDatabaseColumn(JsonObject objectValue)
try
PGobject out = new PGobject();
out.setType("json");
out.setValue(objectValue.toString());
return out;
catch (Exception e)
throw new IllegalArgumentException("Unable to serialize to json field ", e);
@Override
public JsonObject convertToEntityAttribute(Object dataValue)
try
if (dataValue instanceof PGobject && ((PGobject) dataValue).getType().equals("json"))
return mapper.reader(new TypeReference<JsonObject>()
).readValue(((PGobject) dataValue).getValue());
return Json.createObjectBuilder().build();
catch (IOException e)
throw new IllegalArgumentException("Unable to deserialize to json field ", e);
【讨论】:
我已经为 Hibernate 尝试过类似的方法,但似乎以Object
结尾的 AttributeConverter
被 Hibernate
破坏了。我只是收到一个错误,说“未知的 jdbc 类型”或类似的东西。真的很遗憾,因为这种方法更漂亮,样板更少。
从技术上讲,它应该可以工作是对的。 webapp 和服务器没有充分的理由从不同的 ClassLoader 加载类,但他们正在这样做。
此解决方案不适用于 Hibernate(当前为 5.2.10.Final)。错误是org.hibernate.MappingException: No Dialect mapping for JDBC type: SOME_RANDOM_NUMBER
,显然开发人员不会修复它。他们提出的解决方案是使用 Generic Hibernate Types (heavy stuff) vladmihalcea.com/2016/06/20/…
@SewerynNiemiec 你在使用 JPA 吗?此解决方案适用于 JPA,而不适用于 Hibernate 的特定方言 HQL。你的情况是这样吗?
你必须用 mapper.readFor 替换 mapper.read,因为 mapper.read 已被弃用。以上是关于如何在 JPA 中使用 Postgres JSONB 数据类型?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring / JPA 写入 Postgres 数据库的 JSON 列
如何在 Spring Boot 中使用 Hibernate/JPA 返回多级 json
如何使用 Spring Boot JPA 在 Postgres 中存储几何点?