在我们的Hibernate项目中,实体是使用Java beans模式编码的。我们的代码中有一些地方有人忘记设置一个mutator,导致我们由于NOT NULL约束而出现异常。
有人在使用构建器来构建他们的实体或者使它们不可变吗?
我正在尝试找到一种有效的模式,不是基于Java beans模式的风格。
有人在使用构建器来构建他们的实体或者使它们不可变吗?
我正在尝试找到一种有效的模式,不是基于Java beans模式的风格。
如果您将Java beans设置为不可改变的,则必须使用字段级别访问,但这会带来自己的问题,如此处详细讨论的。我们采用的方法是使用构建器/工厂来强制实施/验证所需规则等。
在我们的项目中,我们采用传统的构造器方式(@see Effective Java)。请看下面的例子:
@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;}
}
如果您希望有时更改实体,我建议引入new Builder(Person person)
,它将复制所有数据,因此您可以使用构建器进行更改。当然,它会产生一个新的实体,旧的实体仍然是只读的。
(带突变的)用法非常简单:
Person.Builder personBuilder = new Person.Builder();
Person person = personBuilder.withFullName("Vadim Kirilchuk").withPhone("12345678").build();
Person modified = new Person.Builder(person).withPhone("987654321").build();
需要注意的是,在这个例子中,Person类并不是100%不可变的(也不能完全不可变):首先,id会被JPA设置;另外,懒加载关联可能会在运行时获取;最后,你不能拥有final字段(因为需要默认构造函数):( 最后一点也是多线程环境下的一个问题,即在#build()之后传递实体到另一个线程可能会导致各种错误,因为其他线程无法保证看到完全构建的对象。
JPA 2.1规范第"2.1 The Entity Class"节指出:
实体类的任何方法或持久化实例变量都不能是final。
还有一种类似的方法:http://vlkan.com/blog/post/2015/03/21/immutable-persistence/
在我的情况下,我只会将id添加到构建器中,而不是在草稿上构建服务。
Person
类有一个私有的无参构造函数。它不需要公共的无参构造函数吗? - seal