在我们的hibernate项目中,使用java beans模式对实体进行编码。在我们的代码中有很多地方有人忘记了设置mutator而我们因NOT NULL字段而得到异常。
是否有人使用构建器构建其实体或使其不可变?
我正在尝试找到一种不符合java bean模式风格的有效模式。
由于
答案 0 :(得分:14)
如果你使你的bean不可变,那么你必须使用字段级别的访问权限,这会带来一系列问题,如详细讨论here。我们采取的方法是让Builder / Factory为我们执行/验证必要性等规则。
答案 1 :(得分:10)
在我们的项目中,我们使用vanilla builders方法(@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实体类",说:
实体类的方法或持久性实例变量都不是 最终
另一种类似方法:http://vlkan.com/blog/post/2015/03/21/immutable-persistence/
在我的情况下,我只是将ID添加到构建器,而不是在草稿上构建服务..
答案 2 :(得分:0)
@Immutable
注释可以在Entity上使用。 JPA将忽略对实体所做的所有更新。
https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/Immutable.html
答案 3 :(得分:0)
我认为,逻辑(域)层的责任是防止此类事情在一开始就发生。
我认为您应该具有特定于域的对象,并应用所有业务规则限制并对其进行检查。然后,当您决定保留它们时,首先将它们映射(转换)为实体,然后将它们传递给持久层。
与API级别验证类似,JPA抛出的bean验证异常是防止进一步处理的安全网,在这种情况下,这种处理会导致数据损坏。