我们有一个带有 postgres 枚举的 postgres 数据库。我们开始将 JPA 构建到我们的应用程序中。我们还有 Java 枚举,它反映了 postgres 枚举。现在最大的问题是如何让 JPA 一方面理解 Java 枚举,另一方面理解 postgres 枚举? Java 方面应该相当简单,但我不知道如何做 postgres 方面。
我实际上一直在使用一种比 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,比什么都麻烦。
此时你有两个选择。你要么:
将 stringtype=unspecified 添加到连接字符串,如 JDBC 连接参数中所述。这让 Postgres 猜测右侧类型并充分转换所有内容,因为它接收类似“enum =known”的内容,这是一个它已经知道如何处理表达式(将 ? 值提供给左侧类型反序列化器)。 这是首选选项, 因为它应该一次性适用于所有简单的 UDT,例如枚举。
jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified
或者:
创建从 varchar 到数据库中 enum 的隐式转换。因此,在第二种情况下,数据库收到一些赋值或比较,例如“enum = varchar”,并且它在其内部目录中找到一条规则,表明它可以通过 varchar 的序列化函数传递右侧值,然后通过 varchar 的反序列化函数传递右侧值。枚举。这比实际需要的步骤要多;并且目录中有太多隐式转换可能会导致任意查询产生不明确的解释,因此请谨慎使用。演员阵容是:
创建演员阵容(角色随心情而变化),不隐式使用 INOUT;
应该可以这样工作。
这涉及到进行多个映射。
首先,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
}
这不是一个完整的工作解决方案,而只是一个希望是正确方向的快速指南。
我提交了一份错误报告,其中包含 Hibernate 的补丁:HHH-5188
该补丁可以让我使用 JPA 将 PostgreSQL 枚举读入 Java 枚举。
我尝试了上述建议,但没有成功
我唯一能做的就是将 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
}
这对我来说是工作
@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);
}
}
}
我使用 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,
...
)