The best way to map a Java 1.8 Optional entity attribute with JPA and Hibernate

Introduction

StackOverflow is a neverending source of great questions. This time, we are covering this question about using Java 1.8 Optional with JPA and Hibernate.

Java 1.8 introduced the java.util.Optional container object that may or may not contain a certain value. Combining Optional and streams is very handy. Therefore, you might want that some nullable entity attributes be exposed as Optional.

This article is going to demonstrate what are caveats of using Optional with entity attributes, and how you can overcome them.

Domain Model

Let’s assume we have the following entity model:

optionalattachement

The Post entity is the root in our entity aggregate. There can be multiple PostComment(s) associated with a Post, and each PostComment can have an Attachment. Because the Attachment is not mandatory, it makes sense to use an Optional container for it.

Optional and Serializable

The java.util.Optional does not implement the Serializable interface. For this reason, we should never map an entity attribute as Optional because that will restrict the entity usage.

For instance, we could have a detached instance stored in the HttpSession because we are operating a long conversation workflow. Every object stored in the HttpSession should be Serializable because the session may be clustered on several web nodes.

If you’re using Java EE and Stateful Session Beans, then you must ensure that all entities are `Serializable, as otherwise the passivisation process would fail.

For all these reasons, an entity attribute should not be mapped as a java.util.Optional.

All is not lost

But just because we cannot map an entity attribute as Optional, it does not mean we cannot expose it using an Optional container. If we are using field-based access persistence, then the underlying entity attribute can be mapped using the actual persisted type, while the getter method can use an Optional instead.

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment 
    implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;

    @ManyToOne(fetch = FetchType.LAZY)
    private Attachment attachment;

    public Optional<Attachment> getAttachment() {
        return Optional.ofNullable(attachment);
    }

    public void setAttachment(Attachment attachment) {
        this.attachment = attachment;
    }
    
    //Other getters and setters omitted for brevity
}

That’s it!

If you’re using property-based access, then the getter must expose the actual persisted type, in which case you need to have a separate @Transient method that uses the Optional method return type.

Testing time

Assuming we have the following entities:

byte[] coverContent = new byte[] {1, 2, 3};

Post post = new Post();
post.setId(1L);
post.setTitle("High-Performance Java Persistence");
entityManager.persist(post);

PostComment comment1 = new PostComment();
comment1.setPost(post);

entityManager.persist(comment1);

Attachment cover = new Attachment();
cover.setContent(coverContent);
entityManager.persist(cover);

PostComment comment2 = new PostComment();
comment2.setPost(post);
comment2.setAttachment(cover);

entityManager.persist(comment2);

If we alaready have a list of PostComment(s):

List<PostComment> comments = entityManager.createQuery(
    "select pc " +
    "from PostComment pc " +
    "join pc.post p " +
    "where p.id = :postId", PostComment.class)
.setParameter("postId", 1L)
.getResultList();

We can process Attachment(s)as follows:

Attachment notAvailable = getNotAvaillableImage();

List<Attachment> attachments = comments
.stream()
.map(pc -> pc.getAttachment()
.orElse(notAvailable))
.collect(Collectors.toList());

If there is not Attachment already set, we can use a default N/A image.

If you enjoyed this article, I bet you are going to love my book as well.

Conclusion

When using JPA and Hibernate, you can make use of the Java 1.8 Optional in your Domain Model entities. However, you have to make sure not to use it as a persistent property type.

If you liked this article, you might want to subscribe to my newsletter too.

Advertisements

14 thoughts on “The best way to map a Java 1.8 Optional entity attribute with JPA and Hibernate

  1. Did you only change the getter type to Optional and left the setter type as-is, intentionally? Shouldn’t we change the setter type to Optional, too? Otherwise, I still have to set null values instead of Optional.EMPTY. Furthermore the getter type should match the setter type. What do you think?

    1. That would be a mistake because the entity attribute cannot be an Optional, for the reasons mentioned in the post. Behind the scenes, the attribute can still be null, it’s just that you wrap it with an Optional container.

      1. What I mean is this, the attribute remains of type Attachment but getter AND setter will wrap it into Optional. That should be supported by Hibernate with field access:

        @ManyToOne(fetch = FetchType.LAZY)
        private Attachment attachment;
        
        public Optional getAttachment() {
             return Optional.ofNullable(attachment);
        }
        
        public void setAttachment(Optional attachment) {
            this.attachment = attachment.orElse(null);
        }
        
      2. This will work for sure. I guess it’s a matter of preference in the end. I think that the Optional is more useful for reading, hence the getter has this signature.

        If you chose this Optional setter signature and you need to set the attachment to null, your code would look like this:

        postComment.setAttachement(Optional.ofNullable(null));
        

        So, the setter is equivalent to:

        this.attachment = Optional.ofNullable(null).orElse(null);
        

        I think it’s too much overhead, and I don’t grasp the actual benefit.

      3. Yes it’s a matter of preference. I just wanted to mention it here as an alternative to your proposal. However, I disagree with you on the readability 🙂 because I would rather write:

        postComment.setAttachement(Optional.empty());

        I would stick with a consistent approach because otherwise you have to switch too often between null and Optional.

      1. Actually, it’s not. Optional makes a lot of sense for Streams. For a Value Object, null is much better since it has a direct equivalent in serialized data structures, like Protocol Buffers or JSON.

  2. I agree that using optional in your domain is not bad per definition, but it can be made even better if you implement Null-Object capabilities in your aggregated domain object, meaning it is defaulted to a state indicating that it is empty, not set and will not do anything. Most consuming code would treat it similarly and it should not have any side effects. In general, when looking at your example I think it is far worse that you implement Serializable (why should your domain entities know anything about serialization, this is infrastructural bloat and not a concern of the domain model) and decorate them with annotations. Long lived sessions? They should be avoided if you need to store any great amount of state. Break it up, find natural steps and continue with conversations.

    I would really love reading a blog post about that, why current java practices are bad, that we should stop and think about what we are doing and how we can do it better. There are so many thinks to discuss here, just in this post of yours!

    Coming back to Java after being in other languages for 10 years is like seeing the full-circle come true.
    Back in the days we had entity beans, it was heavyweight development where we had to rely on inheritance to glue the tech stack together. So wrong and so painful. Then thought leaders proclaimed that we should go for lightweight pojo development. The community embraced this and bought into Struts and Spring and Hibernate came along. After a few years we realized that it was xml-hell with all wiring of dependencies in IoC-containers, and XDoclets being the inspiration for annotations. And now we have pulled all the dependencies back into our classes with annotational programming. Haven’t we learned anything???? We are back to early 2000 and my CDI and JPA powered beans are soooo hard to test, not to mention the weight of the containers serving them. Why not stick to pojos?

    But is everything bad? Nope, you just have to use these technologies with care. If you use JPA, please map your code fluently on the outside of your app or trap it where it belongs behind your DAO. Is annotations bad? Nope, not if you define a config corner in your app where you are allowed to wire your app in code. Is setter injection bad, yep, because the alternative is so easily available that it is embarrassing not to use constructors for what they are meant for – fully initializing the instance during creation. Keeping to the SOLID principles you can end up with some infrastructural code with annotations wiring technological glue together while the rest of your microservice is pure pojo. You can now easily do testdriven development and test all of your service, or mock whatever you want.

    The alternative is to fight the tech that should make your life easier everyday and take meds such as Weld or DeltaSpike just to lift the heavy weight EE code into a testable state. I hope someone re-reads the original J2EE Design and Design by Rod Johnson.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s