JPA (EclipseLink) 自定义类型是否可行? [英] Are JPA (EclipseLink) custom types possible?

查看:18
本文介绍了JPA (EclipseLink) 自定义类型是否可行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对使用 PostgreSQLs json 类型特别感兴趣.

问题的核心似乎是Eclipselink没有内部映射到json类型.因此,使用一种简单的方法:

@Column(name = "json", columnDefinition = "json")公共字符串 getJson() {返回json;}

...并试图插入一个对象,我得到一个异常:

内部异常:org.postgresql.util.PSQLException:错误:json"列的类型是 json 但表达式的类型是不同的字符

我想这很公平.

查看 EclipseLink 文档,似乎适用的自定义(转换映射、本机查询、转换器)依赖于由支持的映射(数字、日期、字符串等)组成的数据,因此这使得这很尴尬避免使用供应商特定类型.

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

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

解决方案

遍历 SO 我发现了许多关于 JSON 或 XML 类型映射到 Postgres 的问题.看起来没有人遇到过从自定义 Postgres 类型读取的问题,所以这里使用纯 JPA 类型转换机制进行读取和写入的解决方案.

Postgres JDBC 驱动程序将未知(到 Java)类型的所有属性映射到 org.postgresql.util.PGobject 对象中,因此为这种类型制作转换器就足够了.这是实体示例:

@Entity公共课程课程扩展 AbstractEntity {@Column(name = "course_mapped", columnDefinition = "json")@Convert(converter = CourseMappedConverter.class)私人课程映射课程映射;//不知道为什么要使用 String json 而不是对象来映射//getter 和 setter}

这里是转换器示例:

@Converter公共类 CourseMappedConverter 实现了 AttributeConverter<CourseMapped, PGobject>{@覆盖公共 PGobject convertToDatabaseColumn(CourseMapped courseMapped) {尝试 {PGobject po = new PGobject();//这里我们告诉 Postgres 使用 JSON 作为类型来处理我们的 jsonpo.setType("json");//这是 Jackson 已经作为依赖项添加到项目中的,它可以是任何 JSON 编组器po.setValue((new ObjectMapper()).writeValueAsString(courseMapped));返回宝;} catch (JsonProcessingException e) {e.printStackTrace();返回空;} catch (SQLException e) {e.printStackTrace();返回空;}}@覆盖公共课程映射 convertToEntityAttribute(PGobject po) {尝试 {return (new ObjectMapper()).readValue(po.getValue(),CourseMapped.class);} catch (IOException e) {e.printStackTrace();返回空;}}}

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

实现 AttributeConverter

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

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

In particular I am interested in using PostgreSQLs json type.

The core of the problem seems to be that there is no internal mapping in Eclipselink to json type. So, using a naive approach with:

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

... and trying to insert an object, I get an exception:

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

Fair enough I suppose.

Looking through the EclipseLink docs, it seems that the applicable customizations (Transformation Mappings, Native Queries, Converters) rely on the data being made up of the supported mappings (numbers, dates, strings etc) so it makes this quite awkward to get around using vendor specific types.

The main reason this is so frustrating is that json type in posgresql is expressed the same as text/varchar and I believe (at the moment, but not forever) is just an alias of that type - therefore the driver is more than capable of transmitting this, it's just validation rules in my way.

In terms of the solution, I don't mind losing portability (in terms of being database agnostic and using vendor specific types) but just want a solution that allows me to use a json type as an attribute on a normal JPA Entity and retain all the other behavior it is accustomed to (schema generation, merge, persist, transactional code

解决方案

Walking through SO I've found many questions like this regarding JSON or XML types for mapping into Postgres. It looks like nobody have faced the problem of reading from custom Postgres type, so here the solution for both reading and writing using pure JPA type conversion mechanism.

Postgres JDBC driver maps all attributes for unknown (to Java) types into org.postgresql.util.PGobject object, so it is enough to make converter for this type. Here is entity example:

@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
}

Here the converter example:

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

If you really need to stick to String JSON representation in your entity, you can make converter like this for String type

implements AttributeConverter<String, PGobject>

Here is very dirty (though working) proof of concept, it also uses fake object serialization to tell JPA that object was changed if it was

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

这篇关于JPA (EclipseLink) 自定义类型是否可行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆