java - PostgreSQL 枚举和 Java 枚举之间的 hibernate 映射

标签 java postgresql enums annotations hibernate-mapping

背景

  • Spring 3.x、JPA 2.0、Hibernate 4.x、Postgresql 9.x。
  • 使用我想映射到 Postgresql 枚举的枚举属性处理 Hibernate 映射类。

问题

在枚举列上使用 where 子句进行查询会引发异常。

org.hibernate.exception.SQLGrammarException: could not extract ResultSet
... 
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: movedirection = bytea
  Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.

代码(高度简化)

SQL:

create type movedirection as enum (
    'FORWARD', 'LEFT'
);

CREATE TABLE move
(
    id serial NOT NULL PRIMARY KEY,
    directiontomove movedirection NOT NULL
);

hibernate 映射类:

@Entity
@Table(name = "move")
public class Move {

    public enum Direction {
        FORWARD, LEFT;
    }

    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "sequenceGenerator", strategy=GenerationType.SEQUENCE)
    @SequenceGenerator(name = "sequenceGenerator", sequenceName = "move_id_seq")
    private long id;

    @Column(name = "directiontomove", nullable = false)
    @Enumerated(EnumType.STRING)
    private Direction directionToMove;
    ...
    // getters and setters
}

调用查询的Java:

public List<Move> getMoves(Direction directionToMove) {
    return (List<Direction>) sessionFactory.getCurrentSession()
            .getNamedQuery("getAllMoves")
            .setParameter("directionToMove", directionToMove)
            .list();
}

hibernate xml 查询:

<query name="getAllMoves">
    <![CDATA[
        select move from Move move
        where directiontomove = :directionToMove
    ]]>
</query>

疑难解答

  • id 而不是枚举查询按预期工作。
  • 没有数据库交互的Java可以正常工作:

    public List<Move> getMoves(Direction directionToMove) {
        List<Move> moves = new ArrayList<>();
        Move move1 = new Move();
        move1.setDirection(directionToMove);
        moves.add(move1);
        return moves;
    }
    
  • createQuery 而不是在 XML 中使用查询,类似于 Apache's JPA and Enums via @Enumerated documentation 中的 findByRating 示例给出了同样的异常(exception)。
  • 使用 select * from move where direction = 'LEFT'; 在 psql 中查询按预期工作。
  • 硬编码 where direction = 'FORWARD' 在 XML 的查询中有效。
  • .setParameter("direction", direction.name()) 没有,与 .setString().setText() 相同,异常更改为:

    Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: movedirection = character varying
    

尝试解决问题

  • 自定义 UserType 根据这个接受的答案 https://stackoverflow.com/a/1594020/1090474 的建议还有:

    @Column(name = "direction", nullable = false)
    @Enumerated(EnumType.STRING) // tried with and without this line
    @Type(type = "full.path.to.HibernateMoveDirectionUserType")
    private Direction directionToMove;
    
  • 使用 Hibernate 的 EnumType 进行映射,由评分较高但未被接受的答案 https://stackoverflow.com/a/1604286/1090474 建议来自与上述相同的问题,以及:

    @Type(type = "org.hibernate.type.EnumType",
        parameters = {
                @Parameter(name  = "enumClass", value = "full.path.to.Move$Direction"),
                @Parameter(name = "type", value = "12"),
                @Parameter(name = "useNamed", value = "true")
        })
    

    看到https://stackoverflow.com/a/13241410/1090474后,有没有第二个参数

  • 尝试像此答案 https://stackoverflow.com/a/20252215/1090474 中那样注释 getter 和 setter .
  • 没有尝试过EnumType.ORDINAL,因为我想坚持使用EnumType.STRING,它不那么脆弱,更灵活。

其他说明

JPA 2.1 类型转换器应该不是必需的,但无论如何都不是一个选项,因为我现在使用的是 JPA 2.0。

最佳答案

您可以通过 Maven Central 使用 Hibernate Types 简单地获取这些类型。依赖:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>

如果您使用以下自定义类型轻松地将 Java Enum 映射到 PostgreSQL Enum 列类型:

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 
            );
        }
    }
}

要使用它,您需要使用 Hibernate @Type 注释对字段进行注释,如下例所示:

@Entity(name = "Post")
@Table(name = "post")
@TypeDef(
    name = "pgsql_enum",
    typeClass = PostgreSQLEnumType.class
)
public static class Post {
 
    @Id
    private Long id;
 
    private String title;
 
    @Enumerated(EnumType.STRING)
    @Column(columnDefinition = "post_status_info")
    @Type( type = "pgsql_enum" )
    private PostStatus status;
 
    //Getters and setters omitted for brevity
}

此映射假定您在 PostgreSQL 中有 post_status_info 枚举类型:

CREATE TYPE post_status_info AS ENUM (
    'PENDING', 
    'APPROVED', 
    'SPAM'
)

就是这样,它就像一个魅力。这是 test on GitHub that proves it .

关于java - PostgreSQL 枚举和 Java 枚举之间的 hibernate 映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27804069/

相关文章:

java - 为什么我的代码返回 403 禁止访问

java - 神经网络反向传播训练中的误差下降太慢

mysql - 用于电子商务 SaaS 的 Postgres 共享架构 Multi-Tenancy 设置

java - 如何使用 JDBC 和 PostgreSQL 获取大数据

ios - 如何在 Swift 中简化几乎相等的枚举扩展

java - 如何在java中获取字母的数字位置?

java - 跨平台卓悦

PostgreSQL,无法更新行(具有行级安全性)

java - 在 java 属性文件中表示数组的更好方法

java - 枚举抛出 "Is Not An Enum Type"