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

问题描述:

我没有找到一种方法来使用JPA(EclipseLink)从PostgreSQL映射JSON和JSONB数据类型。有人使用JPA的这种数据类型,可以给我一些工作的例子吗?如何在JPA中使用Postgres JSONB数据类型?

+0

有某处SO一个例子,让我看看,如果我能找到... – Tobb

所有的答案帮助我达到了为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); 
    } 
    } 
} 
+3

我已经尝试过类似于Hibernate的东西,但似乎以'Object'结尾的'AttributeConverter'与'Hibernate'断开了。我只是得到一个错误,说“未知的jdbc类型”或类似的东西。真的很遗憾,因为这种方法更漂亮,而且锅炉板较少。 – Tobb

+0

昨天我不需要这个问题,当我问到这个问题时,我今天需要它并尝试了你的解决方案,只是发现Postgres JDBC驱动程序想通过一个特殊的方法'setPGobject',它只能手动调用,或者如果你使用'setObject'你必须指定类型为'java.sql.Types.OTHER',这是我做不到的。我修改了驱动程序,将它发送到那里,如果未知和正确的类,只会碰到ClassLoader冲突。如果我在webapp中加载驱动程序,加载时会发生冲突,但不会进行设置。如果我将它加载到'tomcat/lib'目录中,设置时会发生冲突。 – coladict

+0

@coladict我发布的课程不适合你?我不太了解你的问题。如果你看到convertToDatabaseColumn方法,它会返回一个Object,但是这个对象已经是Postgres所需的PGobject类型了,我不认为你需要更多的东西。 –

编辑:我现在看到,这几乎是Hibernate从属。但是,也许你可以找到EclipseLink类似的东西..

我就加什么,我有一个答案,这从另一个发出SO回答,但不管。这将映射到jsonb谷歌gsonJsonObject,但你可以如果需要将其更改为其他内容。要更改为其他内容,请更改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); 
    } 
} 

要使用此功能,这样做:

public class SomeEntity { 

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

此外,你需要设置你的话,表明JAVA_OBJECT = jsonb

registerColumnType(Types.JAVA_OBJECT, "jsonb"); 
+0

感谢@tobb您发布的代码,帮助我找到我的情况的解决方案。 –

我想我找到了一个比喻EclipseLink的Hibernate的UserType。

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

你必须要实现org.eclipse.persistence.mappings.converters.Converter并为你做转换的一类,然后使用@Convert注释上的每个字段,您正在使用的类型。