Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java枚举、JPA和Postgres枚举-如何使它们协同工作?_Java_Postgresql_Jpa - Fatal编程技术网

Java枚举、JPA和Postgres枚举-如何使它们协同工作?

Java枚举、JPA和Postgres枚举-如何使它们协同工作?,java,postgresql,jpa,Java,Postgresql,Jpa,我们有一个带有postgres枚举的postgres DB。我们开始将JPA构建到我们的应用程序中。我们还有Java枚举,它镜像postgres枚举。现在最大的问题是如何让JPA理解一边是Java枚举,另一边是postgres枚举?Java端应该相当简单,但我不确定如何做postgres端。这涉及到多个映射 首先,JDBC驱动程序将Postgres枚举作为PGObject类型的实例返回。此属性的type属性具有postgres枚举的名称,value属性具有其值。(但是序号没有存储,因此从技术上讲

我们有一个带有postgres枚举的postgres DB。我们开始将JPA构建到我们的应用程序中。我们还有Java枚举,它镜像postgres枚举。现在最大的问题是如何让JPA理解一边是Java枚举,另一边是postgres枚举?Java端应该相当简单,但我不确定如何做postgres端。

这涉及到多个映射

首先,JDBC驱动程序将Postgres枚举作为PGObject类型的实例返回。此属性的type属性具有postgres枚举的名称,value属性具有其值。(但是序号没有存储,因此从技术上讲它不再是枚举,因此可能完全没有用处)

无论如何,如果你在Postgres中有这样的定义:


CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
然后,对于具有此枚举类型的列和值为“happy”的行,结果集将包含类型为“mood”且值为“happy”的PGObject

接下来要做的事情是编写一些拦截器代码,它位于JPA从原始结果集读取并设置实体值的位置之间。例如,假设您在Java中有以下实体:


public @Entity class Person {

  public static enum Mood {sad, ok, happy}

  @Id Long ID;
  Mood mood;

}
不幸的是,JPA没有提供一个简单的截取点,您可以在该截取点上执行从PGObject到JavaEnum的转换。然而,大多数JPA供应商对此有一些专有支持。例如,Hibernate对此有TypeDef和Type注释(来自Hibernate annotations.jar)

这些允许您提供一个UserType实例(来自Hibernate core.jar),用于执行实际转换:


public class MyEnumConverter implements UserType {

    private static final int[] SQL_TYPES = new int[]{Types.OTHER};

    public Object nullSafeGet(ResultSet arg0, String[] arg1, Object arg2) throws HibernateException, SQLException {

        Object pgObject = arg0.getObject(X); // X is the column containing the enum

        try {
            Method valueMethod = pgObject.getClass().getMethod("getValue");
            String value = (String)valueMethod.invoke(pgObject);            
            return Mood.valueOf(value);     
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public int[] sqlTypes() {       
        return SQL_TYPES;
    }

    // Rest of methods omitted

}

这不是一个完整的工作解决方案,但只是一个指向正确方向的快速指针。

我提交了一份错误报告,其中包含了Hibernate的补丁:


这个补丁可以让我使用JPA将PostgreSQL枚举读入Java枚举。

实际上,我使用的方法比使用PGObject和转换器的方法更简单。因为在Postgres中,枚举非常自然地转换为文本,所以您只需要让它做它最擅长的事情。如果阿扬不介意的话,我可以借用他关于情绪的例子:

Postgres中的枚举类型:


CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
Java中的类和枚举:

public @Entity class Person {

  public static enum Mood {sad, ok, happy};

  @Enumerated(EnumType.STRING)
  Mood mood;
}

@Enumerated标记表示枚举的序列化/反序列化应该在文本中完成。没有它,它使用int,这比任何东西都麻烦

此时,您有两个选择。你可以:

  • 将stringtype=unspecified添加到连接字符串中,如中所述。这可以让Postgres猜测右侧类型并充分转换所有内容,因为它接收类似“enum=unknown”的内容,这是一个它已经知道如何处理的表达式(将?值提供给左侧类型反序列化器)这是首选选项,,因为它可以一次性用于所有简单的UDT,如枚举

    jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified
    
  • 或:

  • 在数据库中创建从varchar到enum的隐式转换。因此,在第二种情况下,数据库接收一些赋值或比较,如'enum=varchar',并在其内部目录中找到一条规则,表示它可以通过varchar的序列化函数传递右边的值,然后是enum的反序列化函数。这是比应该需要的步骤更多的步骤;目录中有太多的隐式强制转换可能会导致任意查询具有不明确的解释,因此请谨慎使用。演员阵容是:

    创建演员阵容(角色随情绪变化),INOUT为隐式


  • 应该可以这样做。

    我们现在也将系统移动到JPA,但是我们有映射到varchar列的java枚举,但Hibernate仍然没有问题>我们现在也将系统移动到JPA,但是我们有映射到varchar列的java枚举,Hibernate varchar仍然没有问题,在DB中看起来不错,但与DB中的实际枚举相比,您遗漏了两件事:*键入safety,除非您希望向每一列添加检查约束,或对单独的表使用FK,该表将所有varchar值作为PK保存。*速度字符串查找和比较速度较慢,即使在索引时也是如此。因此,无论如何,这是一种权衡。您可以使用varchars,除了上述限制,或者使用本机PG enum并接受非自动映射。p.s。还有第三种方法,按序数映射,但这并不推荐;使代码很难读懂;如果从Java枚举中删除或删除单个常量,则数据库中已经存在的所有序号都可能无效!是否有理由使用反射而不是强制转换?@Martin来防止编译时依赖性。并非每个项目的类路径上都有驱动程序,例如,驱动程序通常位于服务器的某个/lib文件夹中。如果这对您来说不是问题,您就不必使用反射。虽然它不使用序号,但它不会存储最多63个字节的整个字符串。枚举值的大小为4字节。查看link()末尾的实现细节,在4.3.5中,这一点似乎更糟。现在我甚至无法将枚举持久化到Postgres,因为Hibernate似乎坚持将其作为文本或整数写入。这是目前为止最简单的最新实现选项。当枚举用作JPA存储库的参数时,CREATE CAST解决方案似乎不起作用。例如,
    Entity findByMyEnum(MyEnum MyEnum)
    使用自动ddl时,仍会将其创建为整数/字符变量。