如何使用JPA的Postgres JSONB数据类型?

时间:2022-09-15 19:15:42

Im not finding a way to map the JSON and JSONB datatypes from PostgreSQL using JPA (EclipseLink). Is some one using this datatypes with JPA and can give me some working examples?

我没有找到使用JPA (EclipseLink)从PostgreSQL中映射JSON和JSONB数据类型的方法。是否有人使用JPA的数据类型,并且可以给我一些工作示例?

3 个解决方案

#1


11  

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

#2


4  

Edit: I see now that this is pretty much Hibernate dependant. But perhaps you can find something similar for EclipseLink..

编辑:我现在明白了,这是非常依赖Hibernate的。但也许你能找到类似于EclipseLink的东西。

I'll just add what I have as an answer, it originates from another SO answer but whatever.. This will map jsonb to JsonObject of Google gson, but you can change it to something else if needed. To change to something else, change nullSafeGet, nullSafeSetand deepCopy methods.

我把我的答案加起来,它源于另一个答案但不管怎样。这将把jsonb映射到谷歌gson的JsonObject,但是如果需要,您可以将其更改为其他内容。要更改其他内容,可以更改nullSafeGet、nullSafeSetand和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);
    }
}

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

#3


2  

I think I found an analogy to Hibernate's UserType for EclipseLink.

我想我找到了一个类似于Hibernate的eclipse用户类型。

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。转换器并为您进行转换,然后在您使用该类型的每个字段上使用@Convert注释。

#1


11  

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

#2


4  

Edit: I see now that this is pretty much Hibernate dependant. But perhaps you can find something similar for EclipseLink..

编辑:我现在明白了,这是非常依赖Hibernate的。但也许你能找到类似于EclipseLink的东西。

I'll just add what I have as an answer, it originates from another SO answer but whatever.. This will map jsonb to JsonObject of Google gson, but you can change it to something else if needed. To change to something else, change nullSafeGet, nullSafeSetand deepCopy methods.

我把我的答案加起来,它源于另一个答案但不管怎样。这将把jsonb映射到谷歌gson的JsonObject,但是如果需要,您可以将其更改为其他内容。要更改其他内容,可以更改nullSafeGet、nullSafeSetand和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);
    }
}

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

#3


2  

I think I found an analogy to Hibernate's UserType for EclipseLink.

我想我找到了一个类似于Hibernate的eclipse用户类型。

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。转换器并为您进行转换,然后在您使用该类型的每个字段上使用@Convert注释。