How to persist an enumeration effectively? Here is my experience.

I’ll use Wordpress as an example. We all know that every post in Wordpress blog system has a status with possible values: draft, pending-review, published and so on.

There are several common ways to model this post entity in a Java application. We could start by simply saving the status as an integer value in the database, where: draft -> 0, pending-review -> 1, published -> 2.

Version 1: Using Integers (Simple but Unsafe)

@Entity
public class Post {
	private Integer status;

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}
}

This works fine, and clients can use it as follows:

Post post = ...;
	//Draft post
post.setStatus(0);
repository.store(post);

The problem? This approach is inherently unsafe. Clients can set any arbitrary integer value for the status, leading to invalid data in your database.

To improve safety, we might define constants for the status values:

public class PostStatus {
	public static final Integer DRAFT = 0;
	public static final Integer PENDING_REVIEW = 1;
	public static final Integer PUBLISHED = 2;
}

Now, clients can use meaningful names:

Post post = ...;
//Draft post
post.setStatus(PostStatus.DRAFT);
repository.store(post);

However, this is still not robust because it doesn’t enforce the use of the PostStatus class. So, how can we enforce safety?

The natural next step is to force clients to use a defined type by declaring PostStatus as an Enum and using it directly in the Post entity.

Version 2: Using JPA’s Default @Enumerated (Better, but Fragile)

public enum PostStatus {
	DRAFT, PENDING_REVIEW, PUBLISHED;
}

@Entity
public class Post {
	@Enumerated
	private PostStatus status;

	public PostStatus getStatus() {
		return status;
	}

	public void setStatus(PostStatus status) {
		this.status = status;
	}
}

This is certainly better for code safety. Clients must now use a valid PostStatus enum constant:

Post post = ...;
//Draft post
post.setStatus(PostStatus.DRAFT);
repository.store(post);

But wait… there’s a serious limitation here. By default, JPA persists all enum constants by their ordinal (position), starting from 0. This creates a brittle dependency: You cannot change the order of constants, and you can only safely append a new constant to the end. Any structural change will corrupt your existing database data.

How do we solve this limitation while keeping the type safety of an Enum?

Version 3: Custom Value Persistence (The Robust Solution)

The Enum type in Java 5 is powerful, allowing us to add a constructor and specific business logic. By applying this feature, we can decouple the persisted value from the enum’s ordinal by explicitly defining the stored value.

public enum PostStatus {
	DRAFT(0),
	PENDING_REVIEW(1),
	PUBLISHED(2);

	private final Integer value;
	private PostStatus(Integer value) {
		this.value = value;
	}
	public Integer getValue() {
		return value;
	}
}

The Post entity is then rewritten to store and retrieve the custom integer value:

@Entity
public class Post {
	private Integer status;

	public PostStatus getStatus() {
		//TODO we need a helper to convert status value to PostStatus enum
		return ...;
	}

	public void setStatus(PostStatus status) {
		this.status = status.getValue();
	}
}

Excellent!

Now, the clients use type-safe code, and we can add, reorder, or modify constants in PostStatus without ever worrying about breaking existing database entries.

This robust design reminds me of that famous quote from the Java4Ever trailer: “Look how beautiful, robust, secure, portable and scalable it is!”

To make Version 3 truly enterprise-grade, consider implementing a custom JPA AttributeConverter. This pattern hides the conversion logic (Integer to Enum and vice versa) away from the Post entity, leading to much cleaner and more maintainable code, especially when dealing with complex data mappings, like those you might encounter when synchronizing data to systems like Elasticsearch.