JPA(EclipseLink)定制类型是否可能? [英] Are JPA (EclipseLink) custom types possible?

查看:95
本文介绍了JPA(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实体并保留它习惯的所有其他行为(模式生成,合并,持久化,事务代码

解决方案

在SO中,我发现了许多类似这样的问题,它们涉及到要映射到Postgres的JSON或XML类型.似乎没有人遇到过从自定义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

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天全站免登陆