有可能拥有不可变的JPA实体吗?

2ekbmq32  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(137)

在我们的Hibernate项目中,实体是使用Javabeans模式编码的。在我们的代码中有很多地方,有人忘记设置一个mutator,我们得到了一个由于NOT NULL约束的异常。
有没有人使用构建器来构建他们的实体,或者让它们变得不可变?
我试图找到一种有效的模式,它不属于Javabeans模式的风格。

u3r8eeie

u3r8eeie1#

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

yqyhoc1h

yqyhoc1h2#

在我们的项目中,我们确实使用了香草构建器方法(@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),它会复制所有数据,所以你可以用builder改变它。当然,它会产生一个新的实体,所以旧的实体仍然是只读的。
使用(带有突变)就像这样简单:

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实体类”一节中说:
实体类的方法或持久示例变量都不能是final。
还有一个类似的方法:http://vlkan.com/blog/post/2015/03/21/immutable-persistence/
在我的情况下,我只会添加id到建设者,而不是建设服务的顶部草案..

相关问题