【问题标题】:Are JPA (EclipseLink) custom types possible?JPA (EclipseLink) 自定义类型是否可行?
【发布时间】:2013-12-18 19:51:06
【问题描述】:

我对使用 PostgreSQLs 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 实体并保留它习惯的所有其他行为(模式生成、合并、持久化、事务代码

【问题讨论】:

    标签: json postgresql jpa jdbc eclipselink


    【解决方案1】:

    浏览 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;
            }
        }
    }
    

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

    implements AttributeConverter<String, PGobject>
    

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

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

    【讨论】:

    • 感谢它的好例子,我已经使用 Postgres 9.3、EclipseLink 2.6.2、Postgres JDBC 9.4-1208、TomEE 1.7.0 容器进行了检查,它运行良好。我也尝试过使用 XML 数据类型,但不幸的是 XML 类型在从数据库中读取时自动转换为 String 并且没有作为 PGobject 返回(编写使用的 PGobject 对我来说很奇怪),所以在这种情况下你需要使用 Object(可能需要使用小的 instanceof 检查) 不是特定 AttributeConverter 类中的 PGobject。
    • 我仍然会出现这个错误:错误:列“my_column”是 json 类型,但表达式是 bytea 类型。知道为什么吗?
    • 如何实现泛型转换器!
    【解决方案2】:

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

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

    This Stack Overflow search 会找到一堆与xml 类型相关的问题,人们也有类似的问题。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    • 2020-08-24
    • 2023-03-24
    • 2021-06-30
    • 2013-10-01
    • 2016-06-19
    相关资源
    最近更新 更多