简体   繁体   English

如何将 Postgres JSONB 数据类型与 JPA 一起使用?

[英]How to use Postgres JSONB datatype with JPA?

Im not finding a way to map the JSON and JSONB datatypes from PostgreSQL using JPA (EclipseLink).我没有找到使用 JPA (EclipseLink) 从 PostgreSQL 到 map JSON 和 JSONB 数据类型的方法。 Is some one using this datatypes with JPA and can give me some working examples?是否有人将此数据类型与 JPA 一起使用,可以给我一些工作示例吗?

All the answers helped me to reach the final solution that is ready for JPA and not EclipseLink or Hibernate specifically.所有的答案都帮助我找到了为 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);
    }
  }
}

Edit: I see now that this is pretty much Hibernate dependent.编辑:我现在看到这几乎依赖于Hibernate But perhaps you can find something similar for EclipseLink .但也许您可以为EclipseLink找到类似的东西。

I'll just add what I have as an answer, it originates from another SO answer but whatever.我只会添加我所拥有的作为答案,它源自另一个 SO 答案,但无论如何。 This will map jsonb to JsonObject of Google gson , but you can change it to something else if needed.这会将jsonb映射到 Google gson JsonObject ,但您可以根据需要将其更改为其他内容。 To change to something else, change nullSafeGet , nullSafeSet and deepCopy methods.要更改为其他内容,请更改nullSafeGetnullSafeSetdeepCopy方法。

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);
    }
}

To use this, do:要使用它,请执行以下操作:

public class SomeEntity {

    @Column(name = "jsonobject")
    @Type(type = "com.myapp.JsonbType") 
    private JsonObject jsonObject;

In addition, you need to set your dialect to indicate that JAVA_OBJECT = jsonb :此外,您需要设置您的方言以指示JAVA_OBJECT = jsonb

registerColumnType(Types.JAVA_OBJECT, "jsonb");

I think I found an analogy to Hibernate's UserType for EclipseLink.我想我找到了一个类比 Hibernate 的 UserType for EclipseLink。

http://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/annotations_ref.htm#CHDEHJEB http://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/annotations_ref.htm#CHDEHJEB

You have to make a class that implements org.eclipse.persistence.mappings.converters.Converter and does the conversion for you, then use the @Convert annotation on every field where you are using that type.您必须创建一个实现org.eclipse.persistence.mappings.converters.Converter并为您进行转换的类,然后在您使用该类型的每个字段上使用@Convert注释。

For anyone looking for a Mysql solution with the JSON column type, here it is.对于正在寻找 JSON 列类型的 Mysql 解决方案的任何人,这里是。 FWIW I am using EclipseLink but this is a pure JPA solution. FWIW 我正在使用 EclipseLink,但这是一个纯粹的 JPA 解决方案。

@Column(name = "JSON_DATA", columnDefinition="JSON")
@Convert(converter=JsonAttributeConverter.class)
private Object jsonData;

and

@Converter
public class JsonAttributeConverter implements AttributeConverter <Object, String>
{

    private JsonbConfig cfg = new JsonbConfig().withFormatting(true);

    private Jsonb jsonb = JsonbBuilder.create(cfg);
    
    @Override
    public String convertToDatabaseColumn(Object object)
    {      
        if (object == null) return null;

        return jsonb.toJson(object);
    }

    @Override
    public Object convertToEntityAttribute(String value)
    {
        if (value == null) return null;

        return jsonb.fromJson(value, value.getClass());
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM