Java 枚举、JPA 和 Postgres 枚举 - 如何让它们一起工作?

问题描述 投票:0回答:6

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

java postgresql jpa
6个回答
76
投票

我实际上一直在使用一种比 PGObject 和转换器更简单的方法。由于在 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=unspecified 添加到连接字符串,如 JDBC 连接参数中所述。这让 Postgres 猜测右侧类型并充分转换所有内容,因为它接收类似“enum =known”的内容,这是一个它已经知道如何处理表达式(将 ? 值提供给左侧类型反序列化器)。 这是首选选项, 因为它应该一次性适用于所有简单的 UDT,例如枚举。

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

或者:

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

    创建演员阵容(角色随心情而变化),不隐式使用 INOUT;

应该可以这样工作。


31
投票

这涉及到进行多个映射。

首先,JDBC 驱动程序返回 Postgres 枚举作为 PGObject 类型的实例。它的 type 属性具有 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

}

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


4
投票

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

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


1
投票

我尝试了上述建议,但没有成功

我唯一能做的就是将 POSTGRES def 设为 TEXT 类型,并在实体中使用 @Enumerated(STRING) 注释。

Ex(Kotlin 语言):

CREATE TABLE IF NOT EXISTS some_example_enum_table
(
    id                      UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    some_enum               TEXT NOT NULL,
);

Kotlin 类示例:

enum class SomeEnum { HELLO, WORLD }

@Entity(name = "some_example_enum_table")
data class EnumExampleEntity(
    @Id
    @GeneratedValue
    var id: UUID? = null,

    @Enumerated(EnumType.STRING)
    var some_enum: SomeEnum = SomeEnum.HELLO,
)

然后我的 JPA 查找实际上起作用了:

@Repository
interface EnumExampleRepository : JpaRepository<EnumExampleEntity, UUID> {
    fun countBySomeEnumNot(status: SomeEnum): Int
}

1
投票

这对我来说是工作

@org.hibernate.annotations.TypeDef(name = "enum_type", typeClass = PostgreSQLEnumType.class)
public class SomeEntity {

...

@Enumerated(EnumType.STRING)
@Type(type = "enum_type")
private AdType name;

}

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.EnumType;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;

public class PostgreSQLEnumType extends EnumType {

@Override
public void nullSafeSet(PreparedStatement ps, Object obj, int index,
                        SharedSessionContractImplementor session) throws HibernateException, SQLException {
    if (obj == null) {
        ps.setNull(index, Types.OTHER);
    } else {
        ps.setObject(index, obj.toString(), Types.OTHER);
    }
 }
}

0
投票

我使用 Kotlin + JPA + PG

PG DDL 是:

create type user_role_enum as enum ('CREATOR', 'OWNER');

我对这个案例的解决方案是这样的: 我将 stringtype=unspecified 添加到我的数据库 URL:

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

我的枚举:

enum class Role {
    OWNER,
    CREATOR
}

和实体:

class UserEntity(
...
    @Enumerated(EnumType.STRING)
    @Column(name = "role", columnDefinition = "user_role_enum")
    val role: Role = Role.OWNER,
...
)
© www.soinside.com 2019 - 2024. All rights reserved.