Java enums map cleanly to database columns with modern Jakarta Persistence and Hibernate. The important decision is not whether Hibernate can persist an enum, but how the enum should be represented in the database.

For most applications, the safest default is explicit: annotate the enum field with @Enumerated(EnumType.STRING). That stores values such as ROCK, PAPER and SCISSORS instead of fragile numeric ordinals.

Modern Java 25 enum example

The enum itself needs no Hibernate-specific code:

package com.mcnz.rps;

public enum Gesture {
  ROCK,
  PAPER,
  SCISSORS
}

Map enums with Jakarta Persistence

Modern Hibernate uses the jakarta.persistence API. Older examples that import javax.persistence predate the Jakarta namespace migration and should be updated.

Here is a compact entity suitable for a modern Java 25 and Hibernate application:

package com.mcnz.rps;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import java.time.Instant;

@Entity
public class GameSummary {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Enumerated(EnumType.STRING)
  private Gesture clientGesture;

  @Enumerated(EnumType.STRING)
  private Gesture serverGesture;

  private String result;

  private Instant playedAt = Instant.now();

  protected GameSummary() {
  }

  public GameSummary(
      Gesture clientGesture,
      Gesture serverGesture,
      String result) {
    this.clientGesture = clientGesture;
    this.serverGesture = serverGesture;
    this.result = result;
  }
}

This version replaces the old java.util.Date approach with java.time.Instant and uses explicit enum mappings so the database representation is obvious from the entity definition.

EnumType.STRING versus EnumType.ORDINAL

If an enum field has no @Enumerated annotation and no converter or other mapping overrides it, enum persistence can fall back to ordinal semantics. That means the position of each constant is stored as a number:

  • ROCK becomes 0
  • PAPER becomes 1
  • SCISSORS becomes 2

Ordinal persistence is compact, but it is brittle. If someone later inserts a new enum constant or reorders the existing constants, old database values can acquire a completely different meaning.

Ordinal values appear with a default JPA or Hibernate enum mapping

Ordinal enum mapping stores numeric positions, which can become unsafe if the enum order changes.

Prefer EnumType.STRING

For most domain enums, explicitly store the enum name:

@Enumerated(EnumType.STRING)
private Gesture clientGesture;

@Enumerated(EnumType.STRING)
private Gesture serverGesture;

The database now stores ROCK, PAPER or SCISSORS. These values are readable and remain stable if the declaration order changes.

Using EnumType STRING to persist a Java enum as text

EnumType.STRING stores the Java enum constant name instead of its ordinal position.

Persist the entity with modern Hibernate

Hibernate's native Session API works well with try-with-resources. The transaction can be expressed clearly without the old EntityManagerFactory boilerplate shown in many legacy tutorials:

var game = new GameSummary(
    Gesture.PAPER,
    Gesture.ROCK,
    "win"
);

try (var session = sessionFactory.openSession()) {
  var transaction = session.beginTransaction();

  try {
    session.persist(game);
    transaction.commit();
  } catch (RuntimeException e) {
    if (transaction.isActive()) {
      transaction.rollback();
    }
    throw e;
  }
}

If your application standardizes on Jakarta Persistence rather than Hibernate's native API, the same entity can be persisted through an EntityManager. The enum annotations are portable Jakarta Persistence mappings and do not tie the entity to Hibernate.

What about hbm.xml enum mappings?

Hibernate still has extensive mapping capabilities, but new applications should normally prefer annotations or Jakarta Persistence XML rather than introducing legacy hbm.xml mappings for a simple enum. The old Hibernate-specific org.hibernate.type.EnumType configuration shown in historical examples is unnecessary for ordinary enum persistence.

If you are maintaining an older application that already uses hbm.xml, there may be good reasons to leave the mapping architecture in place until a planned migration. For new Java 25 code, however, this annotation is usually all that is required:

@Enumerated(EnumType.STRING)
private Gesture gesture;

When the database value should differ from the enum name

EnumType.STRING stores the Java constant name. Sometimes a database schema requires a different stable value, such as R, P and S. In that situation, use a Jakarta Persistence AttributeConverter rather than relying on enum ordinals.

package com.mcnz.rps;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter
public class GestureConverter
    implements AttributeConverter<Gesture, String> {

  @Override
  public String convertToDatabaseColumn(Gesture gesture) {
    if (gesture == null) {
      return null;
    }

    return switch (gesture) {
      case ROCK -> "R";
      case PAPER -> "P";
      case SCISSORS -> "S";
    };
  }

  @Override
  public Gesture convertToEntityAttribute(String value) {
    if (value == null) {
      return null;
    }

    return switch (value) {
      case "R" -> Gesture.ROCK;
      case "P" -> Gesture.PAPER;
      case "S" -> Gesture.SCISSORS;
      default -> throw new IllegalArgumentException(
          "Unknown gesture: " + value
      );
    };
  }
}

The Java 25 switch expression makes the conversion concise and exhaustive. If a new Gesture constant is added, the compiler helps identify code that must be updated.

Modern Hibernate enum mapping recommendations

  • Use jakarta.persistence, not the legacy javax.persistence package.
  • Prefer @Enumerated(EnumType.STRING) for ordinary domain enums.
  • Avoid EnumType.ORDINAL unless the numeric representation is deliberately part of the schema contract.
  • Use an AttributeConverter when the database representation must differ from the Java enum constant name.
  • Use modern java.time types instead of java.util.Date in new entity models.
  • Prefer annotations for new Hibernate applications instead of adding legacy hbm.xml solely to map enums.

Modern JPA and Hibernate enum mapping is therefore straightforward. Make the database representation explicit, favor stable String values, and use a converter when your persistence model needs its own durable codes.

Hibernate and JPA enum mapping meme - enum num num.

Cameron McKenzie Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.

Scrum Master Certification Exam Practice Questions & Answers