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

2024-01-06

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

实体类

  • 实现可序列化

    Reason: 规范规定您必须这样做,但某些 JPA 提供商并不强制执行此操作。 Hibernate 作为 JPA 提供者并不强制执行这一点,但如果尚未实现 Serialized,它可能会在其内心深处因 ClassCastException 而失败。

构造函数

  • 创建一个包含实体所有必需字段的构造函数

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

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

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

字段/属性

  • 一般使用字段访问,并在需要时使用属性访问

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

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

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

等于/哈希码

  • 如果仅在持久化实体时设置此 id,则切勿使用生成的 id
  • 优先选择:使用不可变值形成唯一的业务密钥并使用它来测试相等性
  • 如果唯一的业务密钥不可用,请使用非暂时性的UUID它是在实体初始化时创建的;看这篇很棒的文章 https://web.archive.org/web/20171211235806/http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html了解更多信息。
  • never引用相关实体(ManyToOne);如果此实体(如父实体)需要成为业务密钥的一部分,则仅比较 ID。只要您使用的是,在代理上调用 getId() 就不会触发实体的加载属性访问类型 https://stackoverflow.com/questions/3736818/hibernate-generating-sql-queries-when-accessing-associated-entitys-id/3739197#3739197.

示例实体

@Entity
@Table(name = "ROOM")
public class Room implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    @Column(name = "room_id")
    private Integer id;

    @Column(name = "number") 
    private String number; //immutable

    @Column(name = "capacity")
    private Integer capacity;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "building_id")
    private Building building; //immutable

    Room() {
        // default constructor
    }

    public Room(Building building, String number) {
        // constructor with required field
        notNull(building, "Method called with null parameter (application)");
        notNull(number, "Method called with null parameter (name)");

        this.building = building;
        this.number = number;
    }

    @Override
    public boolean equals(final Object otherObj) {
        if ((otherObj == null) || !(otherObj instanceof Room)) {
            return false;
        }
        // a room can be uniquely identified by it's number and the building it belongs to; normally I would use a UUID in any case but this is just to illustrate the usage of getId()
        final Room other = (Room) otherObj;
        return new EqualsBuilder().append(getNumber(), other.getNumber())
                .append(getBuilding().getId(), other.getBuilding().getId())
                .isEquals();
        //this assumes that Building.id is annotated with @Access(value = AccessType.PROPERTY) 
    }

    public Building getBuilding() {
        return building;
    }


    public Integer getId() {
        return id;
    }

    public String getNumber() {
        return number;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
    }

    public void setCapacity(Integer capacity) {
        this.capacity = capacity;
    }

    //no setters for number, building nor id

}

非常欢迎添加到此列表的其他建议......

UPDATE

自从读了本文 https://web.archive.org/web/20171211235806/http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html我已经调整了实现 eq/hC 的方式:

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

The JPA 2.0 规范 http://www.jcp.org/en/jsr/detail?id=317指出:

  • 实体类必须具有无参数构造函数。它也可能有其他构造函数。无参数构造函数必须是公共的或受保护的。
  • 实体类必须是顶级类。枚举或接口不能是 指定为实体。
  • 实体类不能是最终的。实体类的任何方法或持久实例变量都不能是最终的。
  • 如果实体实例作为分离对象按值传递(例如,通过远程接口),实体类必须实现 Serialized 接口。
  • 抽象类和具体类都可以是实体。实体可以扩展非实体类以及实体类,并且非实体类可以扩展实体类。

据我所知,该规范没有对实体的 equals 和 hashCode 方法的实现提出要求,仅对主键类和映射键进行要求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

创建完美的 JPA 实体 [关闭] 的相关文章

  • Android CursorAdapter、ListView 和后台线程

    我一直在开发的这个应用程序有包含数兆字节数据的数据库可供筛选 许多活动只是列表视图 通过数据库中的各个级别的数据下降 直到到达 文档 即从数据库中提取并显示在手机上的 HTML 我遇到的问题是 其中一些活动需要能够通过捕获击键并重新运行带有
  • Spring @Validated 在服务层

    Hej 我想使用 Validated group Foo class 在执行方法之前验证参数的注释 如下所示 public void doFoo Foo Validated groups Foo class foo 当我将此方法放入 Spr
  • Ant 中回显目标描述

  • Java时间转正常格式

    我有 Java 时间1380822000000 我想转换为我可以阅读的内容 import java util Date object Ws1 val a new Date 1380822000000 toString 导致异常 warnin
  • 如何使用 Swipe 视图实现 Android TabLayout 设计支持库

    我将使用 android TabLayout 设计支持库 但我不知道如何使用滑动视图 这是我的代码 XML
  • 生产者程序中的 kafka 网络处理器错误(ArrayIndexOutOfBoundsException:18)

    我有下面的 kafka Producer Api 程序 我对 kafka 本身是新手 下面的代码从 API 之一获取数据并将消息发送到 kafka 主题 package kafka Demo import java util Propert
  • 在 Java 中的 JFrame/JPanel/JComponent 中添加 Web 浏览器

    我正在开发一个 Java 应用程序 需要在应用程序中使用 Web 浏览器 我见过一些应用程序这样做 例如在同一应用程序中单击左侧面板中的提要并打开右侧面板中的链接时的 RSS 阅读器 我想实现类似的功能 在java中可以做到这一点吗 Jav
  • 使用@Transactional(readOnly = true) 有什么优点?

    我是初学者 据我了解 Transactional只需确保类或方法的所有内部工作都用注释 Transactional将被包装在一个事务中 并且来自外部源的所有调用都将创建一个新事务 但是为什么我们实际上需要在下面的存储库中使用这些注释以及使用
  • Java:不使用 Arrays.sort() 对整数数组进行排序

    这是我们 Java 课程的练习之一中的说明 首先 我想说我 做了我的功课 我不仅仅是懒惰地请 Stack Overflow 上的人帮我回答这个问题 在所有其他练习中 这个特定项目一直是我的问题 因为我一直在努力寻找 完美的算法 编写JAVA
  • 如何构建和使用 TimeSeriesCollections

    我想在图表的 X 轴上显示一些日期 并且here https stackoverflow com questions 5118684 jfreechart histogram with dates据说我必须使用 TimeSeriesColl
  • JPA 的 Hibernate 查询提示

    我一直在尝试为所有可以通过设置的提示找到一个明确的资源Query setHint String Object JPA 中的方法调用 但我一无所获 有人知道一个好的参考吗 See 3 4 1 7 查询提示 http docs jboss or
  • 如何从 Trie 中检索给定长度的随机单词

    我有一个简单的 Trie 用来存储大约 80k 长度为 2 15 的单词 它非常适合检查字符串是否是单词 但是 现在我需要一种获取给定长度的随机单词的方法 换句话说 我需要 getRandomWord 5 来返回 5 个字母的单词 所有 5
  • 如何在启用嵌入时间戳和 LTV 的情况下签署 PDF?

    我正在尝试签署启用了时间戳和 LTV 的 pdf 以便它在 Adob e Reader 中显示如下 在英语中 这意味着 签名包含嵌入的时间戳 和 签名启用了 LTV 这是我正在使用的代码 PrivateKey pk get pk from
  • 应用程序中空指针异常[重复]

    这个问题在这里已经有答案了 我正在尝试在我的应用程序中实施应用程序内计费 我写了这段代码 public class Settings extends PreferenceFragment ServiceConnection mService
  • 动态创建 JSON 对象

    我正在尝试使用以下格式创建 JSON 对象 tableID 1 price 53 payment cash quantity 3 products ID 1 quantity 1 ID 3 quantity 2 我知道如何使用 JSONOb
  • 内部类的访问修饰符[重复]

    这个问题在这里已经有答案了 可能的重复 受保护 公共内部类 https stackoverflow com questions 595179 protected public inner classes 我确信这个问题已经被问过 但我找不到
  • 在java中创建一个XML树并将其转换为json对象

    我尝试创建也能够转换为 json 的树 但对于只有一个xpath 当我尝试实现多个 xpath 时 我无法获得所需的输出 这里我分享一下我的实现 private static Document addElemtbypath List
  • 检查 Java 字符串实例是否可能包含垃圾邮件数据的最简单方法

    我有一个迭代 String 实例的过程 每次迭代对 String 实例执行很少的操作 最后 String 实例被持久化 现在 我想为每次迭代添加一个检查 String 实例是否可能是垃圾邮件的检查 我只需验证 String 实例不是 成人材
  • while循环只执行一次

    我很难弄清楚为什么 while 循环实际上不会循环 它运行一次并停止 import java util public class mileskm public static void main String args Scanner inp
  • 膨胀类 android.support.design.widget.CoordinatorLayoute 时出错

    我正在尝试运行我的应用程序 但不断收到标题中列出的错误 我读过周围的内容 人们说尝试将主题更改为 AppCombat 主题 但这似乎不起作用 以下是我遇到的错误 Process com example jmeyer27 crazytiles

随机推荐