Force use of JPA AttributeConverter for enumerations

We are trying to find a reliable way to save enumerations using JPA. The general approach of using @Enumerated undesirable because it breaks down mappings too easily when refactoring. Each enumeration must have a separate database value, which may differ from the name / order of the enumeration, so that you can safely change the name or internal order (for example, ordinal values) of the enumeration without breaking anything. For instance. this blog post contains an example of how to achieve this, but we believe that the proposed solution adds too much clutter to the code. We would like to achieve a similar result using the new AttributeConverter mechanism introduced in JPA 2.1. We have an interface that every enum must implement, which defines a method to get the value that is used to store the enumeration in the database. Example:

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

We also have a basic converter that has logic for converting enumerations to strings and vice versa, and separate classes of a specific converter for each type of enumeration (AFAIK, the fully universal enum converter cannot be implemented, this is also noted in this SO answer ). The specific converters then simply call the base class that performs the conversion, for example:

 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); } } 

However, although this approach works very well in the technical sense, it is still a rather unpleasant trap: whenever someone creates a new enum class whose values ​​must be stored in the database, this person also needs to remember to create a new enum implements an interface PersistableEnum and writes a converter class for it. Without this, the enumeration will be saved without problems, but the default conversion will use @Enumerated(EnumType.ORDINAL) , which we want to avoid. How can we prevent this? Is there a way to make JPA (in our case, Hibernate) NOT the default for any mapping, but, for example, throw an exception if @Enumerated not defined in the field and there is no converter for the type? Or could we create a catch-all converter that is called for all enumerations that do not have their own converter class and always throw an exception? Or do we just need to suck it and remember the extra steps each time?

+6
source share
1 answer

You want to make sure that all Lists are instances of PersistableEnum.

You need to set the default entity listener (the entity listener whose callbacks apply to all entities in the persistence module).

In the Entity default Listener class implement the @PrePersist method and make sure all the Lists are instances of PersistableEnum.

0
source

Source: https://habr.com/ru/post/970625/


All Articles