Java 如何在 JPA 中使用枚举

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/352586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:43:37  来源:igfitidea点击:

How to use enums with JPA

javajpaenumstoplink

提问by Timo

I have an existing database of a film rental system. Each film has a has a rating attribute. In SQL they used a constraint to limit the allowed values of this attribute.

我有一个电影租赁系统的现有数据库。每部电影都有一个评级属性。在 SQL 中,他们使用约束来限制该属性的允许值。

CONSTRAINT film_rating_check CHECK 
    ((((((((rating)::text = ''::text) OR 
          ((rating)::text = 'G'::text)) OR 
          ((rating)::text = 'PG'::text)) OR 
          ((rating)::text = 'PG-13'::text)) OR 
          ((rating)::text = 'R'::text)) OR 
          ((rating)::text = 'NC-17'::text)))

I think it would be nice to use a Java enum to map the constraint into the object world. But it's not possible to simply take the allowed values because of the special char in "PG-13" and "NC-17". So I implemented the following enum:

我认为使用 Java 枚举将约束映射到对象世界会很好。但是由于“PG-13”和“NC-17”中的特殊字符,不可能简单地采用允许的值。所以我实现了以下枚举:

public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}

@Entity
public class Film {
    ..
    @Enumerated(EnumType.STRING)
    private Rating rating;
    ..

With the toString() method the direction enum -> String works fine, but String -> enum does not work. I get the following exception:

使用 toString() 方法,方向 enum -> String 工作正常,但 String -> enum 不起作用。我收到以下异常:

[TopLink Warning]: 2008.12.09 01:30:57.434--ServerSession(4729123)--Exception [TOPLINK-116] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.DescriptorException Exception Description: No conversion value provided for the value [NC-17] in field [FILM.RATING]. Mapping: oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.RATING] Descriptor: RelationalDescriptor(de.fhw.nsdb.entities.Film --> [DatabaseTable(FILM)])

[TopLink 警告]:2008.12.09 01:30:57.434--ServerSession(4729123)--异常 [TOPLINK-116] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.DescriptorException 异常说明:未为字段 [FILM.RATING] 中的值 [NC-17] 提供转换值。映射:oracle.toplink.essentials.mappings.DirectToFieldMapping[rating-->FILM.RATING] 描述符:RelationalDescriptor(de.fhw.nsdb.entities.Film --> [DatabaseTable(FILM)])

cheers

干杯

timo

蒂莫

采纳答案by Dan Vinton

Sounds like you need to add support for a custom type:

听起来您需要添加对自定义类型的支持:

Extending OracleAS TopLink to Support Custom Type Conversions

扩展 OracleAS TopLink 以支持自定义类型转换

回答by Andreas Petersson

i don't know internals of toplink, but my educated guess is the following: it uses the Rating.valueOf(String s) method to map in the other direction. it is not possible to override valueOf(), so you must stick to the naming convention of java, to allow a correct valueOf method.

我不知道 toplink 的内部结构,但我有根据的猜测如下:它使用 Rating.valueOf(String s) 方法在另一个方向映射。不可能覆盖 valueOf(),因此您必须遵守 java 的命名约定,以允许正确的 valueOf 方法。

public enum Rating {

    UNRATED,
    G, 
    PG,
    PG_13 ,
    R ,
    NC_17 ;

    public String getRating() {
        return name().replace("_","-");;
    }
}

getRating produces the "human-readable" rating. note that the "-" chanracter is not allowed in the enum identifier.

getRating 产生“人类可读”的评级。请注意,枚举标识符中不允许使用“-”字符。

of course you will have to store the values in the DB as NC_17.

当然,您必须将数据库中的值存储为 NC_17。

回答by Mg.

What about this

那这个呢

public String getRating{  
   return rating.toString();
}

pubic void setRating(String rating){  
   //parse rating string to rating enum
   //JPA will use this getter to set the values when getting data from DB   
}  

@Transient  
public Rating getRatingValue(){  
   return rating;
}

@Transient  
public Rating setRatingValue(Rating rating){  
   this.rating = rating;
}

with this you use the ratings as String both on your DB and entity, but use the enum for everything else.

有了这个,你在你的数据库和实体上都使用评级作为字符串,但对其他一切使用枚举。

回答by JeeBee

public enum Rating {

    UNRATED ( "" ),
    G ( "G" ), 
    PG ( "PG" ),
    PG13 ( "PG-13" ),
    R ( "R" ),
    NC17 ( "NC-17" );

    private String rating;

    private static Map<String, Rating> ratings = new HashMap<String, Rating>();
    static {
        for (Rating r : EnumSet.allOf(Rating.class)) {
            ratings.put(r.toString(), r);
        }
    }

    private static Rating getRating(String rating) {
        return ratings.get(rating);
    }

    private Rating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() {
        return rating;
    }
}

I don't know how to do the mappings in the annotated TopLink side of things however.

但是,我不知道如何在带注释的 TopLink 方面进行映射。

回答by cletus

You have a problem here and that is the limited capabilities of JPA when it comes to handling enums. With enums you have two choices:

您在这里遇到了一个问题,那就是 JPA 在处理枚举方面的能力有限。对于枚举,您有两种选择:

  1. Store them as a number equalling Enum.ordinal(), which is a terrible idea (imho); or
  2. Store them as a string equalling Enum.name(). Note:not toString()as you might expect, especially since the default behaviourfor Enum.toString()is to return name().
  1. 将它们存储为一个数字 equaling Enum.ordinal(),这是一个糟糕的主意(恕我直言);或者
  2. 将它们存储为等于Enum.name(). 注意:不像toString()你想象的那样,特别是因为默认行为Enum.toString()是 return name()

Personally I think the best option is (2).

我个人认为最好的选择是(2)。

Now you have a problem in that you're defining values that don't represent vailid instance names in Java (namely using a hyphen). So your choices are:

现在您遇到的问题是,您定义的值不代表 Java 中的有效实例名称(即使用连字符)。所以你的选择是:

  • Change your data;
  • Persist String fields and implicitly convert them to or from enums in your objects; or
  • Use nonstandard extensions like TypeConverters.
  • 更改您的数据;
  • 保留字符串字段并将它们隐式转换为对象中的枚举或从枚举转换;或者
  • 使用像 TypeConverters 这样的非标准扩展。

I would do them in that order (first to last) as an order of preference.

我会按照优先顺序(从头到尾)来做它们。

Someone suggested Oracle TopLink's converter but you're probably using Toplink Essentials, being the reference JPA 1.0 implementation, which is a subset of the commercial Oracle Toplink product.

有人建议使用 Oracle TopLink 转换器,但您可能正在使用 Toplink Essentials,它是参考 JPA 1.0 实现,它是商业 Oracle Toplink 产品的子集。

As another suggestion, I'd strongly recommend switching to EclipseLink. It is a far more complete implementation than Toplink Essentials and Eclipselink will be the reference implementation of JPA 2.0 when released (expected by JavaOne mid next year).

作为另一个建议,我强烈建议切换到EclipseLink。它是比 Toplink Essentials 更完整的实现,并且 Eclipselink 将在发布时成为 JPA 2.0 的参考实现(预计 JavaOne 明年年中)。

回答by aledbf

have you tried to store the ordinal value. Store the string value works fine if you don't have an associated String to the value:

您是否尝试过存储序数值。如果您没有与值关联的字符串,则存储字符串值可以正常工作:

@Enumerated(EnumType.ORDINAL)

回答by Selvaraj Muthiah

use this annotation

使用这个注解

@Column(columnDefinition="ENUM('User', 'Admin')")

回答by pingpong

Resolved!!! Where I found the answer: http://programming.itags.org/development-tools/65254/

解决!!!我在哪里找到答案:http: //programming.itags.org/development-tools/65254/

Briefly, the convertion looks for the name of enum, not the value of attribute 'rating'. In your case: If you have in the db values "NC-17", you need to have in your enum:

简而言之,转换查找枚举的名称,而不是属性“rating”的值。在您的情况下:如果您的数据库值为“NC-17”,则您需要在枚举中包含:

enum Rating {
(...)
NC-17( "NC-17" );
(...)

枚举评级{
(...)
NC-17(“NC-17”);
(……)

回答by YoYo

The problem is, I think, that JPA was never incepted with the idea in mind that we could have a complex preexisting Schema already in place.

我认为问题在于,JPA 从未考虑过我们可以拥有一个复杂的预先存在的架构的想法。

I think there are two main shortcomings resulting from this, specific to Enum:

我认为这有两个主要缺点,特定于 Enum:

  1. The limitation of using name() and ordinal(). Why not just mark a getter with @Id, the way we do with @Entity?
  2. Enum's have usually representation in the database to allow association with all sorts of metadata, including a proper name, a descriptive name, maybe something with localization etc. We need the easy of use of an Enum combined with the flexibility of an Entity.
  1. 使用 name() 和 ordinal() 的限制。为什么不直接用@Id 标记一个getter,就像我们用@Entity 做的那样?
  2. 枚举通常在数据库中具有表示,以允许与各种元数据相关联,包括专有名称、描述性名称、可能具有本地化功能等。我们需要枚举的易用性与实体的灵活性相结合。

Help my cause and vote on JPA_SPEC-47

帮助我的事业并在JPA_SPEC-47上投票

回答by Jayen Chondigara

Enum public enum ParentalControlLevelsEnum { U("U"), PG("PG"), _12("12"), _15("15"), _18("18");

Enum public enum ParentalControlLevelsEnum { U("U"), PG("PG"), _12("12"), _15("15"), _18("18");

private final String value;

ParentalControlLevelsEnum(final String value) {
    this.value = value;
}

public String getValue() {
    return value;
}

public static ParentalControlLevelsEnum fromString(final String value) {
    for (ParentalControlLevelsEnum level : ParentalControlLevelsEnum.values()) {
        if (level.getValue().equalsIgnoreCase(value)) {
            return level;
        }
    }
    return null;
}

}

}

compare -> Enum

比较 -> 枚举

public class RatingComparator implements Comparator {

公共类 RatingComparator 实现 Comparator {

public int compare(final ParentalControlLevelsEnum o1, final ParentalControlLevelsEnum o2) {
    if (o1.ordinal() < o2.ordinal()) {
        return -1;
    } else {
        return 1;
    }
}

}

}