I'm using Java 10. If I define an enum like this
public enum MyEnum {
NAME1("value 1"),
NAME2("value 2"),
...
}
and then I have a JPA-mapped database column that is defined by the enum
@Enumerated(value = EnumType.STRING)
private MyEnum status;
What else do I need to do to map the value of the enum (e.g. "value 2") to the column "status" instead of the name, which is getting mapped currently?
If JPA2 really means JPA 2.1 then AttributeConverter
is the perfect solution.
A class that implements this interface can be used to convert entity attribute state into database column representation and back again. Note that the X and Y types may be the same Java type.
In this case there is an entity
@Entity
@Table(name = "account")
public class Account {
@Id
private Long id;
// Custom converter registered manually
@Convert(converter = AccountTypeConverter.class)
private AccountType type;
private Double balance;
// Getters and setters omitted for brevity
}
and an enumerated field AccountType
public enum AccountType {
DEBIT("D"),
CREDIT("C");
private final String shortValue;
private AccountType(String value) {
this.shortValue = value;
}
public String getShortValue() {
return shortValue;
}
private static final Function<String, AccountType> byShortValue = s ->
Arrays.stream(values())
.filter(e -> e.getShortValue().equals(s))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Invalid account type: " + s));
public static AccountType fromShortValue(String shortValue) {
return Optional.ofNullable(shortValue)
.map(byShortValue)
.orElse(null);
}
}
Finally, the instructed AccountTypeConverter
should be like
public class AccountTypeConverter implements AttributeConverter<AccountType, String> {
@Override
public String convertToDatabaseColumn(AccountType attribute) {
return Optional.ofNullable(attribute)
.map(AccountType::getShortValue)
.orElse(null);
}
@Override
public AccountType convertToEntityAttribute(String dbData) {
return AccountType.fromShortValue(dbData);
}
}
When opening an Account
doInJPA(emFactory, em -> {
var debit = new Account()
.setId(1L)
.setBalance(10_000d)
.setType(AccountType.DEBIT);
var credit = new Account()
.setId(2L)
.setBalance(30_000d)
.setType(AccountType.CREDIT);
em.persist(debit);
em.persist(credit);
});
the generated SQL statements are like this
INSERT INTO account
(balance, type, id)
VALUES (
10000,
'D',
1
);
INSERT INTO account
(balance, type, id)
VALUES (
30000,
'C',
2
);
The previous example shows how to implement and register an attribute converter to a field of an enity. If you have multiple entities with the same type of enumerated field and all occurrences should be converted, then it is possible to auto-register the converter.
Auto-registered AttributeConverter
@Converter(autoApply = true)
public class AccountTypeConverter implements AttributeConverter<AccountType, String> {
// implementation is same
}
In this case annotating the entity field is no longer necessary.
@Entity
@Table(name = "account")
public class Account {
@Id
private Long id;
// Previously registered converter applied automatically
private AccountType type;
private Double balance;
// Getters and setters omitted for brevity
}