How to use enums with JPA

前端 未结 11 1072
梦如初夏
梦如初夏 2020-12-03 02:47

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.

相关标签:
11条回答
  • 2020-12-03 03:10

    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:

    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().

    Personally I think the best option is (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:

    • Change your data;
    • Persist String fields and implicitly convert them to or from enums in your objects; or
    • Use nonstandard extensions like 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.

    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).

    0 讨论(0)
  • 2020-12-03 03:12

    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.

    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.

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

    0 讨论(0)
  • 2020-12-03 03:14

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

    Extending OracleAS TopLink to Support Custom Type Conversions

    0 讨论(0)
  • 2020-12-03 03:20

    Using your existing enum Rating. You can use AttributeCoverters.

    @Converter(autoApply = true)
    public class RatingConverter implements AttributeConverter<Rating, String> {
    
        @Override
        public String convertToDatabaseColumn(Rating rating) {
            if (rating == null) {
                return null;
            }
            return rating.toString();
        }
    
        @Override
        public Rating convertToEntityAttribute(String code) {
            if (code == null) {
                return null;
            }
    
            return Stream.of(Rating.values())
              .filter(c -> c.toString().equals(code))
              .findFirst()
              .orElseThrow(IllegalArgumentException::new);
        }
    }
    
    0 讨论(0)
  • 2020-12-03 03:24
    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.

    0 讨论(0)
提交回复
热议问题