Possible to have immutable JPA entities?

@Immutable annotation can be used on Entity. JPA Will ignore all updates made to entity.

https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/Immutable.html


If you make your beans immutable then you have to use field level access and this comes with its own set of problems as discussed thoroughly here. The approach we took is to have a Builder/Factory enforcing/validating the requiredness etc rules for us.


In our project we do use vanilla builders approach (@see Effective Java). Consider the following example:

@Entity
public class Person {
   public static class Builder {
        private String firstName;
        private String lastName;
        private PhoneNumber phone;

        public Builder() {}

        public Builder withFullName(String fullName) {
            Preconditions.notNull(fullName); 
            String[] split = fullName.split(" ");
            if (split == null || split.length != 2) {
                throw new IllegalArgumentException("Full name should contain First name and Last name. Full name: " + fullName);
            }  
            this.firstName = split[0];
            this.lastName = split[1];
            return this;
        }

        public Builder withPhone(String phone) {
            // valueOf does validation
            this.phone = PhoneNumber.valueOf(phone);
            return this;
        }

        public Person build() {
            return new Person(this);
        }
   }

   //@Columns
   private long id;//@Id
   private String firstName;
   private String lastName;
   private String phoneNumber;

   // hibernate requires default constructor
   private Person() {} 

   private Person(Builder builder) {
       this.firstName = Preconditions.notNull(builder.firstName);
       this.lastName = Preconditions.notNull(builder.lastName);
       this.phoneNumber = builder.phone != null ? builder.phone : null;
   }

   //Getters
   @Nonnull
   public String getFirstName() { return firstName;}
   @Nonnull
   public String getLastName() { return lastName;}
   @Nullable
   public String getPhoneName() { return phone;}
   public long getId() { return id;}
}

In case you want to sometimes mutate the entity I would recomend to introduce new Builder(Person person) which will copy all the data back, so you can mutate it with builder. Of course it will produce new one entity, so the old one remains read only.

The usage (with mutation) is as simple as:

Person.Builder personBuilder = new Person.Builder();
Person person = personBuilder.withFullName("Vadim Kirilchuk").withPhone("12345678").build();

Person modified = new Person.Builder(person).withPhone("987654321").build();

Also it is important to note that in this example Person is not 100% immutable (and can't be) class: first of all because id will be set by jpa, also lazy associations may be fetched at runtime and lastly because you can't have fields final(due to required default constructor) :( The latter point is also a concern for multithreaded environments, i.e. it is possible that entity passed to another thread just after #build() may lead to all kind of errors as abother thread is not guaranteed to see fully constructed object.

The JPA 2.1 specification, section "2.1 The Entity Class", says:

No methods or persistent instance variables of the entity class may be final.

One more similar approach: http://vlkan.com/blog/post/2015/03/21/immutable-persistence/

In my case I would just add id to the builder instead building service on top of drafts..