Effective JPA: Use Enumeration Effectively

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

I will use Wordpress for example. We all know that every post in wordpress blog system has a status with possible values: draft, pending-review, published…

There are some ways to model the post entity. We can save status as an integer value in database: draft -> 0, pending-review -> 1, published -> 2

Version 1

@Entity
public class Post {
	private Integer status;

	public Integer getStatus() {
		return status;
	}

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

Well that’s ok. A client can use:

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

But this is not safe, the client can set any integer value for status. For more save, we can define constants for status values like that:

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

The client can use:

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

But it is still not safe, because the client maybe do not use PostStatus class. So for more safe, what we need to do?
Yes, we can force the client use PostStatus by declaring PostStatus as an Enum and make it dependency in Post class:

Version 2

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;
	}
}

And now client can use it:

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

Well, it is better. But wait…
We know that JPA persists all enum constants in order we declare them in PostStatus with value started from 0. This is limitation, we can not change order of constants, we only can append a new constant. How does we solve this limitation?

Version 3

Enum type in Java 5 is wonderful, we can add constructor and do some business logic in it. Apply it we can re-write PostStatus like this:

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;
	}
}

And Post class is re-wrote like this:

@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();
	}
}

Great!!!

Now client can use code safety and we can add many constants as we want into PostStatus without worry about order of them.

It reminds me of the sentence the guy talked in Java4Ever trailer: “Look how beautiful, robust, secure, portable and scalable it is” ;-)

Comments

8 comments in this post. Add your comments below

  1. This subject is very usefull with JPA. Nice shot!

  2. Thanks Raphael. Hope it helps.

    1. @Entity(value = EnumType.STRING)
    2. public class Post {
    3. @Enumerated
    4. private PostStatus status;
    5.
    6. public PostStatus getStatus() {
    7. return status;
    8. }
    9.
    10. public void setStatus(PostStatus status) {
    11. this.status = status;
    12. }
    13. }

    @Murat It’s nice but it also has limitation. In this case, JPA always persist the enum as string – the name of constants.

    Great site. A lot of useful information here. I’m sending it to some friends!

    Very good!

    Anyone have tried to use murat’ way. I am using the same, but encountering, “unnow enum value for ” exception. Anyone have any idea?

    @lwpro2
    Fix the Murat’s code as following:
    1. @Entity
    2. public class Post {
    3. @Enumerated(EnumType.STRING)
    4. private PostStatus status;
    5.
    6. public PostStatus getStatus() {
    7. return status;
    8. }
    9.
    10. public void setStatus(PostStatus status) {
    11. this.status = status;
    12. }
    13. }

Add comment

  • required
  • required