【问题标题】:Hibernate map enum by property value按属性值休眠映射枚举
【发布时间】:2017-05-28 07:16:03
【问题描述】:

我正在尝试使用 Hibernate 执行以下操作:

  • 当一个对象被保存到数据库时,它被保存为枚举的 name 属性的值。
  • 从数据库中检索对象时,该对象从数据库中读取字符串,并通过枚举的 name 属性值实例化枚举。

让我解释一下。

这是枚举:

public enum Position {

    UNSPECIFIED(1L, "Unspecified"),
    SPECIALIST(2L, "Specialist"),
    NOT_SPECIALIST(3L, "Not Specialist");

    private Long id;
    private String name;

    Position(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public static Position from(Long id) {
        for(Position position: values()) {
            if(position.getId().equals(id))
                return position;
        }

        throw new IllegalArgumentException("Cannot get position for ID " + id);
    }

    public static Position from(String name) {
        for(Position position: values()) {
            if(position.getName().toUpperCase().equals(name.toUpperCase()))
                return position;
        }

        throw new IllegalArgumentException("No such position " + name);
    }
}

这是一个使用枚举的类:

@Entity(name = "worker")
public class Worker {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    private Position position;
    private String email;
}

这是数据在数据库中的显示方式:

worker
id      first_name      last_name       position        email
0       Adam            Applegate       Unspecified     adam@company.com
1       Bob             Barrett         Specialist      bob@company.com

所以基本上,当我调用 workerRepository.findById(0); 时,我返回的工作对象具有以下值:

id --> 0
firstName --> Adam
lastName --> Applegate
position --> Position.UNSPECIFIED
email --> adam@company.com

现在,假设我创建了一个具有以下值的新工作对象:

firstName --> Chad
lastName --> Carlton
position --> Position.NOT_SPECIFIED
email --> chad@company.com

调用workerRepository.save(newWorker); 后,数据库应如下所示:

id      first_name      last_name       position        email
0       Adam            Applegate       Unspecified     adam@company.com
1       Bob             Barrett         Specialist      bob@company.com
2       Chad            Carlton         Not Specialist  chad@company.com

请注意,位置列的值为 Position.NOT_SPECIALIST.getName()。

有没有办法在hibernate中做到这一点?

【问题讨论】:

  • 我想你想要一个AttributeConverter
  • @chrylis 一个快速的谷歌搜索表明 AttributeConverter 可能正是我正在寻找的!稍后我将有更多时间仔细研究它,所以我会告诉你进展如何。
  • 请注意,顺便说一下,除非您可以使用数据库枚举类型(我知道 Postgres 支持这一点),否则这将具有糟糕的搜索性能。
  • 是否每次查询workerRepository都会出现性能下降?或者只有当我通过位置属性查询时才会发生这种情况? (即,位置='未指定')
  • 它只是一个普通的普通字符串列,具有相关的性能问题。除了查询之外,不太可能成为问题(但请记住,它也会占用额外的空间;例如,您的数据库可能会将其外部化)。

标签: java hibernate enums hibernate-mapping


【解决方案1】:

正如@chrylis 建议的那样,我的问题的答案是使用AttributeConverter,如下所示:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = true)
public class PositionConverter implements AttributeConverter<Position, String> {

    @Override
    public String convertToDatabaseColumn(Position attribute) {
        return attribute.getName();
    }

    @Override
    public VolleyballPlayerPosition convertToEntityAttribute(String dbData) {
        return Position.from(dbData);
    }
}

【讨论】:

    【解决方案2】:

    我建议避免使用AttributeConverter,而选择简单的@Enumerated(STRING)

    显然,这仅适用于您可以支持大写和带下划线的位置值。

    public enum Position 
    {
        UNSPECIFIED,
        SPECIALIST,
        NOT_SPECIALIST;
    }
    
    @Entity(name = "worker")
    public class Worker 
    {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        private String firstName;
    
        private String lastName;
    
        @NotNull
        @Enumerated(STRING)
        @Column(nullable = false)
        private Position position = Position.UNSPECIFIED;
    
        private String email;
    }
    
    worker
    id      first_name      last_name       position        email
    0       Adam            Applegate       UNSPECIFIED     adam@company.com
    1       Bob             Barrett         SPECIALIST      bob@company.com
    

    还不清楚为什么您已经拥有Position.ordinal() 时还需要Position.getId():您的Position.from(id) 等同于Position.values()[id]

    最后,在枚举中硬编码声明“标签”不是一个好习惯(通常在编译代码中):只需使用 ResourceBundle,您就可以使用 i18n。

    【讨论】:

    • 不幸的是,这意味着将“未指定”而不是“未指定”写入数据库。事实上,由 position.name 属性定义的值已经存在于数据库中,我无法更改它们。这就是为什么我需要休眠以关闭 position.name 值。
    • Position.ordinal() 而言,position.id 属性存在 bc 序数值是一个:Position.values[1] 将返回 Position.SPECIALIST 当您期望 Position.UNSPECIFIED 时,等等。所以而不是Position.values[1 - 1],我们决定这样做。在考虑我正在使用的系统的限制时,这是最简单的方法。
    猜你喜欢
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 2010-10-18
    相关资源
    最近更新 更多