我们有一个带有postgres枚举的postgres数据库。我们开始在我们的应用程序中构建JPA。我们还有Java枚举,它反映了postgres枚举。现在最大的问题是如何让JPA一方面理解Java枚举,另一方面知道postgres枚举? Java方面应该相当容易,但我不知道如何做postgres方面。
答案 0 :(得分:22)
这涉及进行多次映射。
首先,JDBC驱动程序将Postgres枚举作为PGObject类型的实例返回。它的type属性具有postgres枚举的名称,value属性具有其值。 (但序数并未存储,因此从技术上讲,它不再是枚举,因此可能完全没用)
无论如何,如果你在Postgres中有这样的定义:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
然后结果集将包含一个类型为“mood”的PGObject,值为“happy”,表示具有此枚举类型的列和值为“happy”的行。
接下来要做的是编写一些拦截器代码,它位于JPA从原始结果集读取的位置和设置实体上的值之间。例如。假设你在Java中有以下实体:
public @Entity class Person {
public static enum Mood {sad, ok, happy}
@Id Long ID;
Mood mood;
}
不幸的是,JPA没有提供一个简单的拦截点,您可以在其中执行从PGObject到Java enum 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
}
这不是一个完整的工作解决方案,而只是一个快速指针,希望是正确的方向。
答案 1 :(得分:12)
我实际上使用的方法比使用PGObject和转换器的方式更简单。因为在Postgres中,enum很自然地转换为文本,所以你只需要让它做它最擅长的事情。如果他不介意的话,我会借用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,这比任何东西都麻烦。
此时您有两种选择。你要么:
或者:
创建从varchar到数据库中枚举的隐式转换。因此,在第二种情况下,数据库会收到一些分配或比较,如“enum = varchar'并且它在其内部目录中找到一条规则,说明它可以通过varchar的序列化函数传递右侧值,然后是枚举的反序列化函数。这比我们需要的步骤更多;并且在目录中有太多隐式强制转换可能导致任意查询具有模糊的解释,因此请谨慎使用它。演员创作是:
创建CAST(字符变化为情绪),不作为隐含;
应该只使用它。
答案 2 :(得分:4)
我提交了一份错误报告,其中包含针对Hibernate的补丁:HHH-5188
该补丁适用于我使用JPA将PostgreSQL枚举读入Java枚举。
答案 3 :(得分:0)
我找到了更好的解决方案:
public class PostgreSQLEnumType extends org.hibernate.type.EnumType {
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if(value == null) {
st.setNull( index, Types.OTHER );
}
else {
st.setObject(
index,
value.toString(),
Types.OTHER
);
}
}
}
现在,我们可以如下使用PostgreSQLEnumType
:
@Entity
@TypeDef(name = "mood", typeClass = PostgreSQLEnumType.class)
public class Person {
@Enumerated(EnumType.STRING)
@Type(type = "mood")
Mood mood;
}