How to map Java Enum to custom values with JPA and Hibernate
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to see how we can map Java Enum to custom values when using JPA and Hibernate.
While Hibernate provides several options to save Enum values, having the option to customize this mechanism is even better, as it will allow you to better deal with legacy applications or use cases that require you to reorder the Java Enum values.
Domain Model
Let’s consider we have the following post table:

The status column stores a numerical value associated with a given PostStatus Enum value, but that value is not the typical ordinal value of a Java Enum Object.
The PostStatus Java Enum looks as follows:
public enum PostStatus {
PENDING(100),
APPROVED(10),
SPAM(50),
REQUIRES_MODERATOR_INTERVENTION(1);
⠀
private final int statusCode;
⠀
PostStatus(int statusCode) {
this.statusCode = statusCode;
}
⠀
public int getStatusCode() {
return statusCode;
}
}
What we want to do is store the custom statusCode of the PostStatus Enum instead of typical Java Enum ordinal or name values.
How to map Java Enum to custom values with JPA and Hibernate
By default, Hibernate uses the EnumType to determine whether the Enum name or the ordinal is used to persist the Enum in the underlying database column.
JPA provides the AttributeConverter abstraction to help us deal with situations when we want to control how a given Basic Type is persisted in the database table column.
To achieve our goal of using a custom ordinal value, we are going to use the CustomOrdinalEnumConverter from the Hypersistence Utils project, which looks like this:
public abstract class CustomOrdinalEnumConverter<T>
implements AttributeConverter<T, Integer> {
⠀⠀
private Map<Integer, T> customOrdinalValueToEnumMap = new HashMap<>();
⠀
/**
* Initialization constructor taking the Java Enum to manage.
*
* @param enumType Java Enum type to manage
*/
public CustomOrdinalEnumConverter(Class<T> enumType) {
T[] enumValues = ReflectionUtils.invokeStaticMethod(
ReflectionUtils.getMethod(enumType, "values")
);
for (T enumValue : enumValues) {
Integer customOrdinalValue = convertToDatabaseColumn(enumValue);
customOrdinalValueToEnumMap.put(customOrdinalValue, enumValue);
}
}
⠀
/**
* {@inheritDoc}
*/
@Override
public T convertToEntityAttribute(Integer ordinalValue) {
return customOrdinalValueToEnumMap.get(ordinalValue);
}
}
Now, we need to create the PostStatusConverter that extends the CustomOrdinalEnumConverter base class and implements the convertToDatabaseColumn method:
@Converter
public class PostStatusConverter
extends CustomOrdinalEnumConverter<PostStatus> {
⠀
public PostStatusConverter() {
super(PostStatus.class);
}
⠀
@Override
public Integer convertToDatabaseColumn(PostStatus enumValue) {
return enumValue.getStatusCode();
}
}
Next, we need to instruct Hibernate to use the PostStatusConverter for our PostStatus entity attribute using the @Convert annotation:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
⠀
@Id
private Integer id;
⠀
@Column(length = 250)
private String title;
⠀
@Column(columnDefinition = "NUMERIC(3)")
@Convert(converter = PostStatusConverter.class)
private PostStatus status;
}
That’s it!
Testing Time
When persisting the following Post entities:
entityManager.persist(
new Post()
.setId(1)
.setTitle("To be moderated")
.setStatus(
PostStatus.REQUIRES_MODERATOR_INTERVENTION
)
);
entityManager.persist(
new Post()
.setId(2)
.setTitle("Pending")
.setStatus(
PostStatus.PENDING
)
);
entityManager.persist(
new Post()
.setId(3)
.setTitle("Approved")
.setStatus(
PostStatus.APPROVED
)
);
entityManager.persist(
new Post()
.setId(4)
.setTitle("Spam post")
.setStatus(
PostStatus.SPAM
)
);
Hibernate generates the following SQL INSERT statements:
INSERT INTO post (
status,
title,
id
)
VALUES (
1,
'To be moderated',
1
)
INSERT INTO post (
status,
title,
id
)
VALUES (
100,
'Pending',
2
)
INSERT INTO post (
status,
title,
id
)
VALUES (
10,
'Approved',
3
)
INSERT INTO post (
status,
title,
id
)
VALUES (
50,
'Spam post',
4
)
And when fetching the newly persisted Post entities, we can see that the status attributes are fetched correctly:
assertEquals(
PostStatus.REQUIRES_MODERATOR_INTERVENTION,
entityManager.find(Post.class, 1).getStatus()
);
assertEquals(
PostStatus.PENDING,
entityManager.find(Post.class, 2).getStatus()
);
assertEquals(
PostStatus.APPROVED,
entityManager.find(Post.class, 3).getStatus()
);
assertEquals(
PostStatus.SPAM,
entityManager.find(Post.class, 4).getStatus()
);
Awesome, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
If you want to use a custom ordinal value when persisting and fetching a given Enum value, JPA allows you to use a custom AttributeConverter and provide your own mapping logic.
This mechanism is useful when dealing with legacy applications or if you need to reorder the Enum values. For example, if your application has been previously using the default ordinal values that got persisted in the database, reordering the Enum values will break the application without either updating the existing Enum column values in the post table or using a custom AttributeConverter instance.



JPA 3.2 provides
@EnumeratedValuepublic enum PostStatus { PENDING(100), APPROVED(10), SPAM(50), REQUIRES_MODERATOR_INTERVENTION(1); ⠀ @EnumeratedValue private final int statusCode; ⠀ PostStatus(int statusCode) { this.statusCode = statusCode; } ⠀ public int getStatusCode() { return statusCode; } }see https://jakarta.ee/specifications/persistence/3.2/apidocs/jakarta.persistence/jakarta/persistence/enumeratedvalue
Thanks for the tip