是否可以拥有不可变的 JPA 实体?

问题描述 投票:0回答:3

在我们的 Hibernate 项目中,实体是使用 Java beans 模式进行编码的。我们的代码中有很多地方有人忘记设置变异器,并且由于

NOT NULL
约束而导致异常。

有人使用构建器来构建他们的实体或使它们不可变吗?

我正在尝试找到一种不属于 Java beans 模式风格的有效模式。

hibernate jpa immutability
3个回答
17
投票

如果你让你的bean不可变,那么你必须使用字段级访问,这会带来一系列问题,正如here所讨论的那样。我们采取的方法是让构建器/工厂为我们执行/验证需求等规则。


15
投票

在我们的项目中,我们确实使用普通构建器方法(@参见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 添加到构建器,而不是在草稿之上构建服务..


3
投票

@Immutable
注释可以用在实体上。 JPA 将忽略对实体所做的所有更新。

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

© www.soinside.com 2019 - 2024. All rights reserved.