JPA(EclipseLink)自定义类型是否可行?

时间:2013-12-02 22:57:00

标签: json postgresql jpa jdbc eclipselink

特别是我对使用PostgreSQL json类型感兴趣。

问题的核心似乎是Eclipselink中没有json类型的内部映射。所以,使用一种天真的方法:

@Column(name = "json", columnDefinition = "json")
public String getJson() {
    return json;
}

...并尝试插入一个对象,我得到一个例外:

Internal Exception: org.postgresql.util.PSQLException: ERROR: column "json" is of type json but expression is of type character varying

我认为足够公平。

查看EclipseLink文档,似乎适用的自定义(转换映射,本机查询,转换器)依赖于由支持的映射(数字,日期,字符串等)组成的数据,因此这使得这非常难以理解使用特定于供应商的类型来解决。

令人沮丧的主要原因是posgresql中的json类型表达与text / varchar相同,我相信(目前,但不是永远)只是该类型的别名 - 因此驱动程序能够传输这个,它只是我的验证规则。

就解决方案而言,我不介意丢失可移植性(在数据库不可知和使用供应商特定类型方面),但只是想要一个允许我使用json类型作为属性的解决方案一个普通的JPA实体并保留它习惯的所有其他行为(模式生成,合并,持久化,事务代码

2 个答案:

答案 0 :(得分:25)

走过SO我发现了很多关于JSON或XML类型的问题,用于映射到Postgres。看起来没有人面临从自定义Postgres类型读取的问题,因此这里使用纯JPA类型转换机制进行读写解决方案。

Postgres JDBC驱动程序将未知(到Java)类型的所有属性映射到org.postgresql.util.PGobject对象,因此它足以为此类型生成转换器。这是实体示例:

@Entity
public class Course extends AbstractEntity {
    @Column(name = "course_mapped", columnDefinition = "json")
    @Convert(converter = CourseMappedConverter.class)
    private CourseMapped courseMapped;  // have no idea why would you use String json instead of the object to map

    // getters and setters
}

转换器示例:

@Converter
public class CourseMappedConverter implements AttributeConverter<CourseMapped, PGobject> {
    @Override
    public PGobject convertToDatabaseColumn(CourseMapped courseMapped) {
        try {
            PGobject po = new PGobject();
            // here we tell Postgres to use JSON as type to treat our json
            po.setType("json");
            // this is Jackson already added as dependency to project, it could be any JSON marshaller
            po.setValue((new ObjectMapper()).writeValueAsString(courseMapped));
            return po;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public CourseMapped convertToEntityAttribute(PGobject po) {
        try {
            return (new ObjectMapper()).readValue(po.getValue(),CourseMapped.class);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

如果你真的需要坚持你的实体中的String JSON表示,你可以为String类型制作这样的转换器

implements AttributeConverter<String, PGobject>

这里是非常脏的(虽然有效)概念证明,它还使用假对象序列化来告诉JPA如果对象被更改

https://github.com/sasa7812/psql-cache-evict-POC

答案 1 :(得分:2)

PostgreSQL对类文本类型之间的隐式转换过于严格。最简单的方法是通过创建演员表来解决问题;见this answer

clean 这样做的方法是创建一个调用setObject(my_json)的JPA提供程序扩展,和/或教你的JPA提供程序在生成时显式添加CAST('myvalue' AS json)查询。这很痛苦,因为它需要JPA提供商特定的扩展。

This Stack Overflow search会找到xml类型的一系列相关问题,人们也会遇到类似的问题。