创建完美的JPA实体[已关闭]

我使用JPA(实现Hibernate)已经有一段时间了,每次我需要创建实体时,我都会发现自己在解决访问类型、不可变属性、equals/hashCode等问题。
因此,我决定尝试找出每个问题的一般最佳实践,并将其写下来供个人使用。
不过,我不介意任何人对此发表评论或告诉我哪里错了

实体类

  • 实现可序列化

    原因:规范说您必须这样做,但一些JPA提供程序没有强制执行。作为JPA提供程序的Hibernate没有强制执行这一点,但是如果没有实现Serializable,它可能会在ClassCastException的肚子深处失败。

建设者

  • 使用实体的所有必填字段创建构造函数

    原因:构造函数应始终保持创建的实例处于正常状态。

  • 除了这个构造函数之外:还有一个包私有的默认构造函数

    原因:默认构造函数需要Hibernate初始化实体;允许私有,但运行时代理生成和无字节码检测的高效数据检索需要包私有(或公共)可见性。

字段/属性

  • 通常使用字段访问,必要时使用属性访问

    原因:这可能是最有争议的问题,因为对于其中一个(属性访问与字段访问)没有明确和令人信服的论据;然而,字段访问似乎是最受欢迎的,因为代码更清晰,封装更好,并且无需为不可变字段创建setter

  • 省略不可变字段的设置器(访问类型字段不需要)

  • 属性可能是私有的
    原因:我曾经听说受保护的(Hibernate)性能更好,但我在web上只能找到:Hibernate可以直接访问公共、私有和受保护的访问器方法,以及公共、私有和受保护的字段。选择取决于您,您可以根据您的应用程序设计进行匹配

等于/哈希代码

  • 如果仅在持久化实体时才设置此id,则切勿使用生成的id
  • 按偏好:使用不可变值来形成唯一的业务键,并使用它来测试相等性
  • 如果唯一的业务密钥不可用,请使用在初始化实体时创建的非瞬态UUID;请参阅这篇精彩文章了解更多信息
  • 从不提及相关实体(多个实体);如果此实体(如父实体)需要是业务密钥的一部分,则只比较ID。在代理上调用getId()不会触发实体加载,只要您使用属性访问类型

示例实体

@实体
@表(name=“房间”)
公共教室实现了可序列化{
私有静态最终长serialVersionUID=1L;
@身份证
@生成值
@列(name=“room\u id”)
私有整数id;
@列(name=“number”)
私有字符串编号;//不可变
@列(name=“capacity”)
专用整数容量;
@manytone(fetch=FetchType.LAZY,可选=false)
@JoinColumn(name=“building\u id”)
私人建筑;//不可变
房间(){
//默认构造函数
}
公共房间(建筑物,字符串编号){
//带必填字段的构造函数
notNull(构建,“使用null参数调用的方法(应用程序)”;
notNull(数字,“使用null参数(名称)”调用的方法);
这个建筑=建筑;
这个数字=数字;
}
@凌驾
公共布尔等于(最终对象otherObj){
if((otherObj==null)| |!(房间的otherObj实例)){
返回false;
}
//一个房间可以通过它的编号和它所属的建筑进行唯一标识;通常我会在任何情况下使用UUID,但这只是为了说明getId()的用法
最终房间其他=(房间)其他OBJ;
返回新的EqualBuilder().append(getNumber(),other.getNumber())
.append(getBuilding().getId(),other.getBuilding().getId())
.isEquals();
//这假设Building.id用@Access(value=AccessType.PROPERTY)注释
}
公共建筑{
回归大厦;
}
公共整数getId(){
返回id;
}
公共字符串getNumber(){
返回号码;
}
@凌驾
公共int hashCode(){
返回新的HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
}
公共空间设置容量(整数容量){
这个。容量=容量;
}
//没有编号、建筑或id设置器
}

其他建议添加到此列表中是非常受欢迎的

更新

自阅读后这篇文章我已经调整了实现eq/hC的方式:

  • 如果一个不可变的简单业务密钥可用:使用它
  • 在所有其他情况下:使用uuid

发表评论