当前位置: 首页 > 知识库问答 >
问题:

强制对枚举使用JPA AttributeConverter

谷梁楷
2023-03-14

我们正试图找到一种使用JPA来持久化枚举的可靠方法。使用@枚举的常见方法是不可取的,因为在重构时破坏映射太容易了。每个枚举都应该有一个单独的数据库值,它可以不同于枚举名称/顺序,这样您就可以安全地更改枚举的名称或内部顺序(例如序数值),而不会破坏任何东西。例如,这篇博客文章有一个如何实现这一目标的示例,但是我们觉得建议的解决方案给代码增加了太多的混乱。我们希望通过使用JPA 2.1中引入的新的属性转换器机制来实现类似的结果。我们有一个接口,每个枚举都应该实现,它定义了一个方法来获取用于在数据库中存储枚举的值。示例:

public interface PersistableEnum {
    String getDatabaseValue();
}

...

public enum SomeEnum implements PersistableEnum {
    FOO("foo"), BAR("bar");

    private String databaseValue;

    private SomeEnum(String databaseValue) {
        this.databaseValue = databaseValue;
    }

    public void getDatabaseValue() {
        return databaseValue;
    }
}

我们还有一个基本转换器,它具有将枚举转换为字符串的逻辑,反之亦然,并为每种枚举类型提供单独的具体转换器类(AFAIK,一个完全通用的枚举转换器是不可能实现的,这一点在本SO答案中也有所说明)。然后,具体的转换器只需调用进行转换的基类,如下所示:

public abstract class EnumConverter<E extends PersistableEnum> {

    protected String toDatabaseValue(E value) {
        // Do the conversion...
    }

    protected E toEntityAttribute(Class<E> enumClass, String value) {
        // Do the conversion...
    }
}

...

@Converter(autoApply = true)
public class SomeEnumConverter extends EnumConverter<SomeEnum> 
            implements AttributeConverter<SomeEnum, String> {

    public String convertToDatabaseColumn(SomeEnum attribute) {
        return toDatabaseValue(attribute);
    }

    public SomeEnum convertToEntityAttribute(String dbData) {
        return toEntityAttribute(SomeEnum.class, dbData);
    }
}

然而,虽然这种方法在技术上非常有效,但仍然存在一个相当严重的陷阱:每当有人创建一个新的枚举类,其值需要存储到数据库中时,该人还需要记住让新的枚举实现PersistableEnum接口,并为其编写一个转换器类。如果没有这一点,枚举将得到持久化,而不会出现问题,但是转换将默认使用@Enumerated(EnumType.ORDINAL),这正是我们想要避免的。我们怎样才能防止这种情况?有没有办法使JPA(在我们的例子中是Hibernate)不默认为任何映射,但如果字段上没有定义@Enumerated,并且找不到该类型的转换器,则抛出异常?或者我们可以创建一个“catch all”转换器,它为所有没有自己特定转换器类的枚举调用,并且总是从那里抛出异常?还是说我们每次都要吸取教训,试着记住额外的步骤?

共有1个答案

乐华晖
2023-03-14

您需要设置一个默认实体监听器(一个实体监听器,它的回调应用于持久性单元中的所有实体)。在默认实体监听器类中实现@PrePerig方法,并确保所有的ENums都是PeristableEnum的实例。

 类似资料:
  • 使用 use 声明,这样就不必手动加上作用域了: // 隐藏未使用代码警告的属性。 #![allow(dead_code)] enum Status { Rich, Poor, } enum Work { Civilian, Soldier, } fn main() { // 明确地 `use` 各个名称使他们直接可用而不需要手动加上作用域。 use

  • 当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份: JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12 好处是简单,缺点是类型是int,并且仍然是变量。 更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能: from enum import Enum

  • 枚举类(“新的枚举”/“强类型的枚举”)主要用来解决传统的C++枚举的三个问题: 传统C++枚举会被隐式转换为int,这在那些不应被转换为int的情况下可能导致错误 传统C++枚举的每一枚举值在其作用域范围内都是可见的,容易导致名称冲突(同名冲突) 不可以指定枚举的底层数据类型,这可能会导致代码不容易理解、兼容性问题以及不可以进行前向声明 枚举类(enum)(“强类型枚举”)是强类型的,并且具有类

  • 问题内容: Linux内核2.6 我有一个通过GPIO加载的fpga,该GPIO连接到运行Linux的开发板。fpga将通过pci- express总线发送和接收数据。但是,这是在启动时枚举的,因此,未发现任何链接(因为在启动时未加载fpga)。 如何在Linux中强制重新枚举pci-e总线?是否有一个简单的命令,或者我必须进行内核更改?我需要热插拔PCIe设备的功能。 问题答案: 我想知道您使用

  • 问题内容: 我很清楚Java中枚举的高级用法。对我来说,许多区别于普通班和告诉他们需要的观点也很清楚。 谁能举一些带有高级枚举示例代码的实际示例?可以清楚地阐述一下 我们应该避免使用类,而应该使用枚举 。 请不要混淆。我不是在问如何使用枚举或枚举的用途是什么。关于此有很多问题和答案。我正在寻找一些实时/实时/实际的例子,我们应该避免任何其他数据类型或类。 问题答案: 试试这个例子: 用法:

  • 实际开发中,我们离不开定义常量,当我们需要定义常量时,其中一个办法是用大写变量通过整数来定义,例如月份: JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12 当然这样做简单快捷,缺点是类型是 int ,并且仍然是变量。 那有没有什么好的方法呢? 这时候我们定义一个 class 类型,每个常量都是 class 里面唯一的实例。 正好 Python 提供了 E