Possible to have immutable JPA entities?
In our hibernate project, the entities are coded using the java beans pattern. There's quite a few spots in our code where someone has forgotten a to set a mutator and we get an exception due to a NOT NULL field.
Is anyone using a builder to construct their entities or making them immutable?
I'm trying to find an effective pattern that is not in the style of the java beans patt开发者_C百科ern.
Thanks
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..
@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
精彩评论