How to Define Custom Enum Mappings with @EnumeratedValue
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
Mapping enums with Jakarta Persistence looks simple at first. But the two traditional options, @Enumerated(EnumType.STRING) and @Enumerated(EnumType.ORDINAL), depend on internal enum details that can change over time.
If you store the enum value as a String, you will have to migrate your data whenever you decide to rename a value. And if you store the ordinal, reordering the enum values and adding or removing any value except for the last one, changes the ordinal of some enum values. So, you’ll have to migrate your database to adjust the stored enum values. These issues are common in long-lived projects where enums evolve along with business rules.
Before Jakarta Persistence 3.2, the only safe way to avoid these problems was to define your own mapping using an AttributeConverter. As I showed in a previous article, many teams used such an AttributeConverter to define a fixed mapping for each enum value.
Jakarta Persistence 3.2 and Hibernate 7.0+ finally made this easier. The new @EnumeratedValue annotation lets you define a field that will be mapped to the database instead of the ordinal or String representation. This makes your enum mappings explicit, self-contained, and much safer to maintain.
Defining a Custom Mapping using @EnumeratedValue
Let’s use the @EnumeratedValue annotation to define a custom mapping for the following PlayerType enum. Instead of the standard mapping based on enum values’ names or ordinals, I want to store the value of the type field in the database.
public enum PlayerType {
Hobby("H"), Professional("P");
@EnumeratedValue
final String type;
PlayerType(String type) {
this.type = type;
}
}
Each enum value defines a fixed and unique type value. Hibernate uses this value to map an enum value to its corresponding database value when writing or reading the entity. If two enum values shared the same type value, Hibernate wouldn’t be able to determine which constant to use when reading data from the database.
As long as you keep these values unique and consistent, you can safely rename, reorder, add, or remove enum constants without breaking the mapping or requiring a data migration.
Use the @EnumeratedValue mapping
You can use the PlayerType enum in your entity like any other enum. Here’s an example
@Entity
public class ChessPlayer {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
@Enumerated(EnumType.STRING)
private PlayerType playerType;
// getters and setters
}
Let’s persist a ChessPlayer entity object and check how Hibernate maps it to the database.
ChessPlayer player = new ChessPlayer();
player.setFirstName("Thorben");
player.setLastName("Janssen");
player.setPlayerType(PlayerType.Hobby);
em.persist(player);
15:41:35,008 DEBUG [org.hibernate.SQL] -
select
nextval('ChessPlayer_SEQ')
15:41:35,019 TRACE [org.hibernate.orm.jdbc.batch] - Created JDBC batch (5) - [com.thorben.janssen.model.ChessPlayer#INSERT]
15:41:35,019 TRACE [org.hibernate.orm.jdbc.batch] - Adding to JDBC batch (1 / 5) - [com.thorben.janssen.model.ChessPlayer#INSERT]
15:41:35,020 DEBUG [org.hibernate.SQL] -
insert
into
ChessPlayer
(firstName, lastName, playerType, id)
values
(?, ?, ?, ?)
15:41:35,021 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter (1:VARCHAR) <- [Thorben]
15:41:35,021 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter (2:VARCHAR) <- [Janssen]
15:41:35,021 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter (3:VARCHAR) <- [H]
15:41:35,021 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter (4:BIGINT) <- [2]
The third bind parameter of the INSERT statement shows that Hibernate stored "H" for the Hobby constant. So, it used the value of the type field, which I annotated with @EnumeratedValue, instead of the enum’s String or ordinal representation.
Summary
The @EnumeratedValue annotation in Jakarta Persistence 3.2, supported since Hibernate 7, provides a clean and stable way to map enums. It eliminates the need for an AttributeConverter and allows you to change your enum without having to migrate your database. This makes it a small improvement but one of my favorite changes in Jakarta Persistence 3.2 and Hibernate 7.

