当前位置: 首页 > 面试题库 >

PostgreSQL枚举和Java枚举之间的休眠映射

龚远
2023-03-14
问题内容

背景

  • Spring 3.x,JPA 2.0,Hibernate 4.x,Postgresql 9.x.
  • 使用我想映射到Postgresql枚举的enum属性在Hibernate映射的类上工作。

问题

用enum列上的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与使用@Enumerated文档通过Apache的JPA和Enums中的findByRating示例类似,而不是使用XML查询。

  • 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;

  • 根据与上述相同的问题,EnumType由评分较高但未被接受的答案https://stackoverflow.com/a/1604286/1090474建议使用Hibernate进行映射,以及:

    @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。
  • 尚未尝试过,EnumType.ORDINAL因为我想坚持使用EnumType.STRING,它不那么脆,而且更灵活。

其他注意事项

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


问题答案:

高品质

正确别名并使用 合格的属性名称 是解决方案的第一部分。

    <query name="getAllMoves">
        <![CDATA[
            from Move as move
            where move.directionToMove = :direction
        ]]>
    </query>

休眠映射

@Enumerated(EnumType.STRING)仍然无法正常工作,因此需要自定义UserType。关键是要正确覆盖nullSafeSet此答案中的网络实现。

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
    if (value == null) {
        st.setNull(index, Types.VARCHAR);
    }
    else {
        st.setObject(index, ((Enum) value).name(), Types.OTHER);
    }
}

车辆改道

implements ParameterizedType 没有合作:

org.hibernate.MappingException: type is not parameterized: full.path.to.PGEnumUserType

所以我不能这样注释enum属性:

@Type(type = "full.path.to.PGEnumUserType",
        parameters = {
                @Parameter(name = "enumClass", value = "full.path.to.Move$Direction")
        }
)

相反,我这样声明了该类:

public class PGEnumUserType<E extends Enum<E>> implements UserType

使用构造函数

public PGEnumUserType(Class<E> enumClass) {
    this.enumClass = enumClass;
}

不幸的是,这意味着任何其他类似映射的枚举属性都需要这样的类:

public class HibernateDirectionUserType extends PGEnumUserType<Direction> {
    public HibernateDirectionUserType() {
        super(Direction.class);
    }
}

注解

注释属性,您就完成了。

    @Column(name = "directiontomove", nullable = false)
    @Type(type = "full.path.to.HibernateDirectionUserType")
    private Direction directionToMove;

其他注意事项

  • EnhancedUserType 以及它想要实现的三种方法
        public String objectToSQLString(Object value)
    public String toXMLString(Object value)
    public String html" target="_blank">objectToSQLString(Object value)

没什么区别,所以我坚持了implements UserType

  • 根据您使用类的方式,可能不一定要通过重写nullSafeGet两个链接的解决方案的方式来使其特定于Postgres 。
  • 如果您愿意放弃postgres枚举,则可以创建该列text,并且原始代码可以正常运行而无需额外的工作。


 类似资料:
  • Spring 3.x、JPA 2.0、Hibernate 4.x、Postgresql 9.x. 使用希望映射到Postgresql枚举的枚举属性处理Hibernate映射类。 使用枚举列上的where子句进行查询会引发异常。 SQL: Hibernate xml查询: > 按而不是按枚举查询工作正常。 没有数据库交互的Java工作良好: 不是,与和相同,异常更改为: 在查看https://sta

  • 问题内容: 我需要预先将没有实现接口的枚举映射到现有数据库,该数据库使用将该枚举存储在与所有者类相同的表中。 在这种情况下应如何处理映射?持久化到数据库不会改变,因为实现该接口的所有枚举都将具有不同的值,但是我不确定应如何从数据库中检索对象(我是否需要自定义映射器,它将尝试实例化一个使用指定的enum类进行枚举吗?Hibernate是否本身支持此功能?)。 问题答案: 可以创建一个自定义(例如th

  • 问题内容: Hibernate提供的注释支持使用或两种类型的映射。当我们使用映射时,它使用的“名称” 而不是Enum 的表示形式。在数据库列仅包含一个字符的情况下,这是一个问题。例如,我有以下枚举: 当我坚持枚举使用,即休眠尝试在数据库中存储的值是开放的。但是,我的数据库列仅包含一个字符,因此会引发异常。 克服这个问题的一个办法是改变枚举类型持有单个字符(如,代替,)。但是,这降低了可读性。有什么

  • 问题内容: 在我的MySQL数据库中,有“ gender enum(’male’,’female’)”列 我创建了枚举“ com.mydomain.myapp.enums.Gender”,并在我的实体中定义了“性别”。 现在,我想将枚举类型保留在我的MySQL数据库中,但是当我启动应用程序时,我得到: 性别列在MyApp.Person中的列类型错误。找到:枚举,预期:整数 为什么是这样?就像我用“

  • 问题内容: Enumeration <有区别吗?扩展ZipEntry>和Enumeration ?如果是这样,有什么区别? 问题答案: 拥有其中一种后,您在做什么上没有实际差异,因为type参数仅在“输出”位置使用。另一方面,在您可以 用作 其中一个的方面有很大的不同。 假设您有一个-您无法将其传递给作为其参数之一的方法。您 可以 将其传递给采用方法。 当您有一个在输入和输出位置都使用type参数

  • 问题内容: 枚举具有获取枚举常量的方法,并且在具有 我发现的名称的类中存在的相同类型的方法都给出相同的输出。那还有什么其他区别。如果没有区别,那么为什么要添加JSL ? 问题答案: 包括该方法的原因是它可以与任何方法一起使用。相比之下, 用于特定方法的方法仅适用于该特定方法…,因为类不能被多态使用。 显然,该方法仅在您实现 需要 针对多种类型使用的代码的情况下才真正有用……而泛型则不会削减它。