我们有一个带有Postgres枚举的Postgres数据库。我们正在将JPA集成到我们的应用程序中。我们还拥有与Postgres枚举相对应的Java枚举。现在最大的问题是如何让JPA理解Java枚举和Postgres枚举。Java方面应该很容易,但我不确定如何处理Postgres方面。
我们有一个带有Postgres枚举的Postgres数据库。我们正在将JPA集成到我们的应用程序中。我们还拥有与Postgres枚举相对应的Java枚举。现在最大的问题是如何让JPA理解Java枚举和Postgres枚举。Java方面应该很容易,但我不确定如何处理Postgres方面。
实际上我使用的方法比PGObject和转换器更简单。因为在Postgres中,枚举类型可以很自然地被转换为文本,所以你只需要让它发挥最大作用即可。如果Arjan不介意,我会借用他的情绪示例:
在Postgres中的枚举类型:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
Java中的类(class)和枚举(enum):public @Entity class Person {
public static enum Mood {sad, ok, happy};
@Enumerated(EnumType.STRING)
Mood mood;
}
那个@Enumerated标签表示应该使用文本进行枚举的序列化/反序列化。如果没有它,就会使用int,这比任何其他方式都更麻烦。
此时你有两个选择。你可以:
在连接字符串中添加stringtype=unspecified,如JDBC connection parameters中所解释的那样。这使得Postgres可以猜测右侧的类型并适当地转换所有内容,因为它接收到的是类似于“enum = unknown”的表达式,它已经知道如何处理(将?值提供给左侧类型的反序列化器)。这是首选项,因为它应该可以一次性适用于所有简单的用户定义类型,如枚举。
jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified
或:
在数据库中创建一个从 varchar 到 enum 的隐式转换。所以在第二种情况下,数据库接收到一些类似于 'enum = varchar' 的赋值或比较,然后在其内部目录中找到一条规则,说它可以通过 varchar 的序列化函数和枚举的反序列化函数来传递右侧的值。这比需要更多步骤;并且在目录中有太多的隐式转换会导致任意查询具有模棱两可的解释,因此要谨慎使用。强制转换的创建方式为:
CREATE CAST (CHARACTER VARYING as mood) WITH INOUT AS IMPLICIT;
应该只需要这样就可以了。
Entity findByMyEnum(MyEnum myEnum)
。 - Daniele Repici那么结果集将包含一个类型为“mood”,值为“happy”的PGObject,对于具有此枚举类型并具有值“happy”的行的列。
下一步是编写一些拦截器代码,它坐在JPA从原始结果集读取并在实体上设置值的位置之间。例如,假设您在Java中有以下实体:
公共实体类 Person {
公共 静态 枚举类型 Mood {伤心的,一般般的,开心的}
@Id 长整型 ID;
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;
这段代码是Java中与数据库相关的实体类代码示例,其中使用了一个自定义枚举类型的转换器。在这个示例中,Person类表示数据库中的一个人,其中心情(mood)属性的类型为自定义枚举类型Mood,并使用名为“myEnumConverter”的转换器进行数据类型转换。这些允许您提供一个UserType实例(来自Hibernate-core.jar),该实例执行实际的转换:
public class MyEnumConverter implements UserType {
private static final int[] SQL_TYPES = new int[]{Types.OTHER};
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
// 从结果集中获取对应枚举值
Object pgObject = resultSet.getObject(X); // X 为包含枚举值的列名
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;
}
// 其他方法省略
}
说明:此代码段是一个实现了 Hibernate 框架 UserType 接口的自定义枚举类型转换器,用于将数据库中存储的枚举值转换为 Java 中的枚举类型。具体实现逻辑是从结果集中获取对应的枚举对象,利用反射获取其值并进行转换。这不是一个完整的工作解决方案,而只是一个快速指向正确方向的提示。
这对我有用
@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);
}
}
}
我尝试了上述建议,但没有成功。
唯一使其正常工作的方法是将POSTGRES def定义为TEXT类型,并在实体中使用@Enumerated(STRING)注解。
示例(使用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
}