Java Enums,JPA和Postgres enums - 我如何使它们一起工作?

2022-08-31 20:14:17

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


答案 1

实际上,我一直在使用比PGObject和Transfers更简单的方法。由于在Postgres枚举中,枚举是很自然地转换为文本的,因此您只需要让它做它最擅长的事情即可。如果他不介意的话,我会借用Arjan的情绪例子:

Postgres 中的枚举类型:

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,这比任何东西都麻烦。

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

  1. stringtype=unspeced 添加到连接字符串中,如 JDBC 连接参数中所述。这让Postgres猜测右侧类型并充分转换所有内容,因为它会收到类似“enum = unknown”的东西,这是一个它已经知道该怎么做的表达式(将?值提供给左侧类型 deserialiser)。这是首选选项,因为它应该可以一次性适用于所有简单的 UDT,例如枚举。

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

艺术

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

    创建演员表(角色随情绪而变化),其中INOUT为隐含;

应该用它来工作。


答案 2

这涉及进行多个映射。

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

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


CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

然后,结果集将包含一个 PGObject,其类型为“mood”,值为“happy”,用于具有此枚举类型的列和一行,其值为“happy”。

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


public @Entity class Person {

  public static enum Mood {sad, ok, happy}

  @Id Long ID;
  Mood mood;

}

不幸的是,JPA没有提供一个简单的拦截点,您可以在其中进行从PGObject到Java枚举Mood的转换。但是,大多数 JPA 供应商对此都有一些专有支持。例如,Hibernate为此提供了TypeDef和Type注释(来自Hibernate-annotations.jar)。


@TypeDef(name="myEnumConverter", typeClass=MyEnumConverter.class)
public @Entity class Person {

  public static enum Mood {sad, ok, happy}

  @Id Long ID;
  @Type(type="myEnumConverter") Mood mood;

这些允许您提供执行实际转换的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

}

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


推荐