JavaRush /Java 博客 /Random-ZH /JPA 实体和数据库关系
Nikita Koliadin
第 40 级
Днепр

JPA 实体和数据库关系

已在 Random-ZH 群组中发布

JPA 实体和数据库关系

美好的一天,同事们!
JPA 实体和数据库关系 - 1
本材料面向那些已经了解数据库组织(以下简称DB - “数据库”)、对对象关系映射如何工作(以下简称ORM)及其实现(例如Hibernate / JPA)有最少了解的人。如果您对此不熟悉,我建议您从JDBC开始,然后再转向 ORM 模型。我警告过你,我不对你在没有充分准备的情况下阅读本文后的心理负责!:) 让我们开始按顺序处理所有事情。首先,我们将深入研究一下理论,只是一点点。其次,我们将弄清楚如何在每个人最喜欢的 Java 中做这些事情。我们还将与您一起编写一份项目备忘单,它将巩固我们对该主题的理解,并作为应该如何完成映射的模板。那么,让我们开始吧!

什么是实体?

实体现实生活中的对象(例如汽车),具有属性(门、车轮、发动机)。DB实体:在这种情况下,我们的实体存储在数据库中,一切都很简单。我们为什么以及如何将汽车放入数据库 - 我们稍后会看到。

什么是数据库关系?

很久以前,在遥远的王国,创建了一个关系型数据库。在该数据库中,数据以表格的形式呈现。但即使是史莱克的驴子也明白,有必要创建一种机制来互连这些桌子。结果出现了4个DB关系
  1. 一对一
  2. 一对多
  3. 多对一
  4. 多对多
如果您是第一次看到这一切,我再次警告您 - 情况会变得更糟:考虑出去散步。我们将通过一个例子来分析所有这些关系,并理解它们之间的区别。

恐怖的例子

我们将有一个项目,该项目将有 5 个分支:master,其中将有项目的描述,以及每个数据库关系的 1 个分支。每个分支将包含用于创建数据库并用测试数据填充它的 SQL 脚本,以及带有注释映射的实体类。每个分支还将有一个 Hibernate 配置文件。我将在项目中使用H2嵌入式数据库,以免被云数据库或外部数据库的各个方面分散注意力。通过点击链接,在吸尘器上安装 H2 DB。我将在1个分支中描述每个步骤,其余的只是要点。最后我们来总结一下。去。 是我项目的主分支的链接。

一对一的关系

链接到此处的分支。
  1. 我们需要将 H2 DB 连接到我们的项目。这里我们需要强调的是,我们需要Ultimate IDEA才能轻松地与DB和其他东西一起工作。如果已经有了,那么直接进入数据库连接。转到数据库选项卡并按照屏幕截图中的操作进行操作:

    JPA Entities and DB Relationships - 2

    接下来我们继续进行数据库设置。您可以输入您的数据,甚至您的 DBMS;我重复一遍,为了简单起见,我使用 H2 DB。

    JPA Entities and DB Relationships - 3

    接下来,让我们设置电路。此步骤是可选的,但如果数据库中有多个架构,则建议执行此步骤。

    JPA Entities and DB Relationships - 4

    应用设置,最后我们应该得到这样的结果:

    JPA Entities and DB Relationships - 5
  2. 我们创建了数据库并配置了从 IDEA 对其的访问。现在您需要在其中创建表并填充一些数据。例如,我将采用两个实体:作者和书籍。一本书可能有一个作者,可能有多个作者,也可能没有一个作者。在此示例中,我们将创建所有类型的连接。但此时——一对一的关系。让我们创建相应的脚本来创建数据库表

    DROP TABLE IF EXISTS PUBLIC.BOOK;
    
    CREATE TABLE PUBLIC.BOOK (
      ID         INTEGER      NOT NULL AUTO_INCREMENT,
      NAME       VARCHAR(255) NOT NULL,
      PRINT_YEAR INTEGER(4)   NOT NULL,
      CONSTRAINT BOOK_PRIMARY_KEY PRIMARY KEY (ID)
    );
    
    DROP TABLE IF EXISTS PUBLIC.AUTHOR;
    
    CREATE TABLE PUBLIC.AUTHOR (
      ID          INTEGER      NOT NULL AUTO_INCREMENT,
      FIRST_NAME  VARCHAR(255) NOT NULL,
      SECOND_NAME VARCHAR(255) NOT NULL,
      BOOK_ID     INTEGER      NOT NULL UNIQUE,
      CONSTRAINT AUTHOR_PRIMARY_KEY PRIMARY KEY (ID),
      CONSTRAINT BOOK_FOREIGN_KEY FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID)
    );

    让我们执行它:

    JPA Entities and DB Relationships - 6

    控制台执行结果:

    JPA Entities and DB Relationships - 7

    数据库中的结果:

    JPA Entities and DB Relationships - 8
  3. 让我们看一下表格的图表。为此,我们的数据库上的人民币:

    JPA Entities and DB Relationships - 9

    结果:

    JPA Entities and DB Relationships - 10

    UML图上我们可以看到所有的主键和外键,我们还可以看到表之间的连接。

  4. 让我们编写一个脚本,用测试数据填充我们的数据库:

    INSERT INTO PUBLIC.BOOK (NAME, PRINT_YEAR)
    VALUES ('First book', 2010),
           ('Second book', 2011),
           ('Third book', 2012);
    
    INSERT INTO PUBLIC.AUTHOR (FIRST_NAME, SECOND_NAME, BOOK_ID)
    VALUES ('Pablo', 'Lambado', 1),
           ('Pazo', 'Zopa', 2),
           ('Lika', 'Vika', 3);

    我的意思是,会发生什么?当一个表的实体与另一个表的一个实体相关(或者如果从 BOOK_ID 中删除 NOT NULL 则根本不相关)时,需要一对一关系。在我们的示例中,一本书必须有一位作者。别无退路。

  5. 现在最有趣的是如何将Java类与DB实体连接起来?很简单。让我们创建两个类 Book 和 Author。我将通过一个例子来分析第一类和关键的通信领域。我们以Author类为例:

    @Data
    @Entity
    @DynamicInsert
    @DynamicUpdate
    @Table(name = "AUTHOR")
    public class Author {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "ID", nullable = false)
        private Long id;
    
        @Column(name = "FIRST_NAME", nullable = false)
        private String firstName;
    
        @Column(name = "SECOND_NAME", nullable = false)
        private String secondName;
    
        @OneToOne
        @JoinColumn(name = "BOOK_ID", unique = true, nullable = false)
        private Book book;
    }
我们按顺序来算一下:
  1. 类中的所有字段都重复数据库实体的属性。
  2. @Data(来自Lombok)表示,对于每个字段,将创建一个 getter 和 setter,equals、hashcode 将被覆盖,并生成一个 toString 方法。
  3. @Entity表示给定的类是一个实体并与数据库实体关联。
  4. @DynamicInsert@DynamicUpdate表示动态插入和更新将在数据库中执行。这些是更深层次的 Hibernate 设置,对您有用,以便您获得正确的批处理。
  5. @Table (name = "AUTHOR") 将 Book 类绑定到 DB AUTHOR 表。
  6. @Id表示该字段是主键。
  7. @GenerateValue (strategy = GenerationType.IDENTITY) – 主键生成策略。
  8. @Column (name = "ID", nullable = false) 将一个字段与一个 DB 属性关联起来,同时也表示给定的 DB 字段不能为 null。从实体生成表时这也很有用。与我们现在创建项目的方式相反的过程,这在单元测试的测试数据库中是必需的。
  9. @OneToOne表示给定字段是一对一关系字段。
  10. @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - 将创建一个 BOOK_ID 列,该列是唯一的且不为空。
在反面(在Book类中),我们还需要建立一对一连接并指示发生映射的字段。@OneToOne(mappedBy = "book") - 在此示例中,这是 Author 类的 book 字段。JPA 将自行链接它们。乍一看,注释似乎很乱,但实际上它非常方便,随着经验的积累,你会不假思索地自动添加它们。
  1. 现在让我们配置 Hibernate。为此,请创建一个hibernate.cfg.xml文件:

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
        <session-factory>
            <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
            <property name="hibernate.connection.driver_class">org.h2.Driver</property>
    
            <property name="hibernate.connection.url">jdbc:h2:~/db/onetoone</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password"/>
    
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <property name="hibernate.show_sql">true</property>
            <property name="hibernate.format_sql">true</property>
            <property name="hibernate.use_sql_comments">true</property>
    
            <property name="hibernate.generate_statistics">true</property>
    
            <property name="hibernate.jdbc.batch_size">50</property>
            <property name="hibernate.jdbc.fetch_size">50</property>
    
            <property name="hibernate.order_inserts">true</property>
            <property name="hibernate.order_updates">true</property>
            <property name="hibernate.jdbc.batch_versioned_data">true</property>
    
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Book"/>
            <mapping class="com.qthegamep.forjavarushpublication2.entity.Author"/>
        </session-factory>
    </hibernate-configuration>
属性说明
  1. hibernate.dialect 是我们选择的 DBMS 的方言。
  2. hibernate.connection.driver_class - 我们的数据库的驱动程序类。
  3. hibernate.connection.url - 我们数据库的 utl。你可以从我们配置数据库的第一点开始。
  4. hibernate.connection.username - 数据库用户名。
  5. hibernate.connection.password — 数据库用户密码。
  6. hibernate.hbm2ddl.auto - 设置表生成。如果是update,那么如果已经创建了就不会生成,而只是更新它。
  7. hibernate.show_sql - 是否显示数据库查询。
  8. hibernate.format_sql - 是否格式化数据库查询。如果没有,那么他们都会在一条线上。我建议打开它。
  9. hibernate.use_sql_comments - 注释数据库查询。如果这是一个Insert,则在请求上方写一条注释,表明该请求是Insert类型的。
  10. hibernate.generate_statistics - 生成日志。我推荐并建议将日志记录设置为最大。阅读日志将增加您正确使用 ORM 的机会。
  11. hibernate.jdbc.batch_size — 最大批量大小。
  12. hibernate.jdbc.fetch_size — 最大获取大小。
  13. hibernate.order_inserts - 允许动态插入。
  14. hibernate.order_updates - 允许动态更新。
  15. hibernate.jdbc.batch_versioned_data - 允许批处理。看看你的 DBMS:不是每个人都支持这一点。
  16. 映射类 - 作为我们实体的类。所有的事情都需要列出来。
  1. 现在我们必须确定我们的本质。我们可以在持久性选项卡中检查这一点:

    JPA Entities and DB Relationships - 11

    结果:

    JPA Entities and DB Relationships - 12
  2. 我们还需要配置分配数据:

    JPA Entities and DB Relationships - 13 JPA Entities and DB Relationships - 14

    结果:我们完成了一对一映射。该材料仅供参考,详细信息请参见参考资料。

一对多关系

链接到此处的分支。文章中的代码我就不再贴出来了,因为代码已经太长了。我们查看 GitHub 上的所有代码。
  1. 执行初始化脚本的结果是:

    JPA Entities and DB Relationships - 15

    你感觉和上一张表有什么不同吗?

  2. 图表:

    JPA Entities and DB Relationships - 16

    一对多关系 - 一位作者可以拥有多本书。左侧实体对应于一个或多个右侧实体。

  3. 映射的区别在于注释和字段:

    Author类中出现一个字段:

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "author")
    private Set<Book> books;

    这已经是一套了,因为我们可以有几本书。@OneToMany正在谈论态度类型。FetchType.Lazy 表示如果请求中未指定,我们不需要加载整个图书列表。还应该说的是,这个字段不能添加到 toString 中,否则我们将开始产生 StackOverflowError。我心爱的 Lombok 负责处理这个问题:

    @ToString(exclude = "books")

    Book类中,我们进行多对一反馈:

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "AUTHOR_ID", nullable = false)
    private Author author;

    在这里我们得出结论,一对多是多对一的镜像,反之亦然。应该强调的是,Hibernate 对双向通信一无所知。对他来说,这是两种不同的联系:一种是同向的,另一种是相反的。

  4. hibernate.cfg.xml中没有发生太大变化。

  5. 持久性:

    JPA Entities and DB Relationships - 17

多对一关系

由于多对一是一对多的镜像,因此几乎没有差异。链接到此处的分支。
  1. 执行初始化脚本的结果是:

    JPA Entities and DB Relationships - 18
  2. 图表:

    JPA Entities and DB Relationships - 19
  3. 映射的区别在于注释和字段:

    Author类中不再有集合,因为它已移至Book类。

  4. 休眠配置文件

  5. 持久性:

    JPA Entities and DB Relationships - 20

多对多关系

让我们继续讨论最有趣的关系。根据所有正派和不正派规则,这种关系是通过一个附加表创建的。但这个表不是一个实体。有趣,对吧?我们来看看这个狗屎。链接到此处的分支。
  1. 查看初始化脚本,这里出现了一个额外的HAS表。我们得到类似作者有书的东西。

    执行脚本的结果是,我们将得到下表:

    JPA Entities and DB Relationships - 21
  2. 图表:

    JPA Entities and DB Relationships - 22

    在我们的例子中,事实证明,一本书可以有很多作者,而一个作者可以有很多书。它们可能会重叠。

  3. 映射类将在类内有集合。但正如我所说,HAS 表不是一个实体。

    作者类别:

    @ManyToMany
    @JoinTable(name = "HAS",
            joinColumns = @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"),
            inverseJoinColumns = @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID")
    )
    private Set<Book> books;

    @ManyToMany是一种关系类型。

    @JoinTable - 这正是将属性与附加 HAS 表连接起来的方法。在其中我们指定两个属性,它们将指向两个实体的主键。

    图书类别:

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "books")
    private Set<Author> authors;

    在这里,我们指示 FetchType 和我们将用于映射的字段。

  4. 我们的hibernate.cfg.xml再次保持不变(我没有考虑我们为每个分支创建一个新数据库的事实)。

  5. 持久性:

    JPA Entities and DB Relationships - 23

汇报

因此,我们已经粗略地研究了数据库关系的类型,并弄清楚了如何在 ORM 模型中实现它们。我们编写了一个测试项目来演示所有连接,并弄清楚如何配置 hibernate / jpa。唷。

有用的链接

我之前的文章: PS 文中可能存在错误和不足之处。PPS 作者在写这篇文章时抽的是奇怪的东西。感谢您的关注!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION