Java 使用JPA时构建器模式的最佳实践和实现

Java 使用JPA时构建器模式的最佳实践和实现,java,hibernate,jpa,design-patterns,builder,Java,Hibernate,Jpa,Design Patterns,Builder,我有一个适合构建器模式的类,有许多参数,我不想使用大量伸缩构造函数 我的问题是这个类是一个JPA实体,这对我来说是非常新的 拥有私有最终数据成员会引发错误,因为它们没有在构造函数中初始化,而且据我所知,JPA需要一个空的受保护构造函数 有人能帮忙吗?举个例子会很棒,我在下面提供了一个基本的代码示例,但它非常通用。为了节省空间/时间,我省略了许多访问器和数据成员 @Entity//(name= "TABLE_NAME") //name of the entity / tab

我有一个适合构建器模式的类,有许多参数,我不想使用大量伸缩构造函数

我的问题是这个类是一个JPA实体,这对我来说是非常新的

拥有私有最终数据成员会引发错误,因为它们没有在构造函数中初始化,而且据我所知,JPA需要一个空的受保护构造函数

有人能帮忙吗?举个例子会很棒,我在下面提供了一个基本的代码示例,但它非常通用。为了节省空间/时间,我省略了许多访问器和数据成员

  @Entity//(name= "TABLE_NAME") //name of the entity / table name
public class Bean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id //primary key
    @GeneratedValue
    Long id;
    private final DateTime date;
    private final String title;
    private final String intro;


    //used by jpa
    protected Bean(){}

    private Bean(Bean Builder beanBuilder){
        this.date = beanBuilder;
        this.title = beanBuilder;

        this.intro = beanBuilder;
    }

    public DateTime getDate() {
        return date;
    }

    public String getTitle() {
        return title;
    }

    public static class BeanBuilder Builder{

        private final DateTime date;
        private final String title; 

        //private optional 

        public BeanBuilder(DateTime date, String title) {
            this.date = date;
            this.title = title;
        }

        public BeanBuilder intro(String intro){
            this.intro = intro;
            return this;
        }

        public BeanBuilder solution(String solution){
            this.intro = solution;
            return this;
        }

        public Bean buildBean(){
            return new Bean(this);
        }

    }

}

标记为
final
的成员字段必须在构造期间指定一个值,并且该值为final(即不能更改)。因此,all声明的构造函数必须为all
final
字段分配一个值

这可以解释您的编译器错误

从:

必须在声明该类的每个构造函数的末尾明确指定一个空的最终实例变量,否则会发生编译时错误(§8.8,§16.9)


我不知道你的意思是什么,但是在Hibernate中使用不可变对象并不是一个好主意(并不是说你不能这样做,或者你不应该这样做)

想想看,因为Hibernate/JPA为对象定义了“”,它们是可变的;否则,您将拥有一个静态数据库,或者类似于insert once的东西,并且永远不会修改数据库

不变的概念是一个非常有名的(现在)概念,它主要是从函数式编程中借用的,而函数式编程并没有以同样的方式应用于OOP。如果你使用Hibernate,你不应该有不可变的对象……至少直到今天

更新


如果您想拥有他们所谓的只读实体,可以使用Hibernate本身的注释。请密切关注作为实体成员的集合。

不确定为什么要这样做。也许最好将成员变量定义为
@Column(name=“id”,nullable=false,updateable=false)

例如,JPA 2.1规范的“2.1实体类”一节说:

不能使用实体类的任何方法或持久实例变量 决赛


…这意味着你无法构建一个真正不变的JPA实体。但是,我真的不明白这怎么会是一个大问题。只是不要让实体类公开公共setter?

当涉及到严格的Java不变性时,实体是可变的。例如,延迟加载的关联将在访问关联后更改对象状态


如果您需要以一种真正不可变的方式使用实体数据(例如,对于多线程目的),那么考虑使用DTOs(因为实体也不意味着CunNeo也可以访问)。也许,或者我不希望对象是可变的……我不会有一个接受构建器的构造函数。这样,构建器就不负责构建对象。该类的用途仅仅是作为一个容器(可以说是带有硬编码属性的映射!)。@MartinAndersson-看起来我在复制/粘贴代码时失败了。构造函数应该采用一个生成器,然后将属性设置为生成器的值。然后我可以使用build.x(“valueofx”).builder.y(val)之类的东西,然后正在讨论的对象将设置这些值,但是如果没有设置y,则它将为null,这也很好。至少我是这样理解的……这可能是非常错误的:)@Steve Green使类不可变就像不在该对象上提供突变行为并保持所有字段私有一样简单。诚然,您可能会意外地从类内部修改一个字段,但从您的客户机的角度来看,该类是不可变的。您知道有什么方法可以保持该类的不可变性,并且仍然将其用于JPA吗?据我所知,JPA需要一个空构造函数。但是,它不能像那样简单…?Hibernate提供了一个

@Immutable
注释,我知道你在说什么,但在这种情况下,我真的不希望在插入数据库后对它们进行修改。我使用JPA/Hibernate作为SQL的替代品只是为了获得一些曝光,它可能不是这项工作的最佳工具。:)@SteveGreen如果您真的知道要在数据库中插入的任何内容都不会改变,那么您可以这样做,并使用名为
Immutable
(或类似的东西…我真的不记得了)的注释,但这就像拥有一个只读实体谢谢。我从你们那里得到的印象是,我试图做的是一个坏主意:)我试图创建一个干净的方式,通过使用生成器生成不同的网站页面。例如,一种类型可能有2个图像,另一种类型可能有4个图像。我的偏好是根据类型使用一个tsp来处理它们,但也许有更好的方法:)@uıuɥuɔuɐɯ“想想看,因为Hibernate/JPA定义了对象的“状态”,它们是可变的”。那远远不是真的。。。在领域驱动的设计中,不可变对象(值对象)非常常见,甚至在可能的情况下通常比实体更受欢迎。从域的角度来看,只要一个类不向它的客户机提供变异行为并且完全封装,那么这个类或概念从客户机的角度来看是不变的。根本不需要
@Immutable
注释,尽管它可能会提高性能(不确定)。@plalx我不想在这里开始一个无休止的线程,但您谈论的是完全不同的东西,这是比较VO和任何Hibernate实体的要点。VO是包含属性但没有概念“标识”的对象(不要混淆上下文中的“标识”)