JavaRush /Java Blog /Random EN /JPA Entities and DB Relationships
Nikita Koliadin
Level 40
Днепр

JPA Entities and DB Relationships

Published in the Random EN group

JPA Entities && DB Relationships

Good day, colleagues!
JPA Entities and DB Relationships - 1
This material is intended for those who already have an understanding of the organization of databases (hereinafter simply DB - "Database"), minimal knowledge of how Object-Relational Mapping works (hereinafter simply ORM ), and its implementations, such as Hibernate / JPA . If you are not familiar with this, I advise you to start with JDBC , and only then move on to the ORM model. I warned you, and I do not bear responsibility for your psyche after reading this article without proper preparation! :) Let's start to deal with everything in order. Firstly, we will delve a little into the theory, just a little bit. Secondly, we will figure out how to do this shit in everyone’s favorite Java. We will also write with you a project-cheat sheet, which will consolidate our understanding of the topic and serve as a template for HOW mapping should be done . So, Let's do it!

What is Entity?

An Entity is an object from real life (for example, a car) that has attributes (doors, WHEELS , engine). DB Entity: In this case, our entity is stored in a DB, everything is simple. Why and how we put the car into the database - we’ll look at it later.

What is DB Relationships?

A long time ago, in the distant kingdom , a relational DB was created . In this DB, data was presented in the form of tables. But it was also clear to the donkey from Shrek that it was necessary to create a mechanism for interconnecting these tables. As a result, 4 DB relationships appeared :
  1. One-to-one
  2. One-to-Many
  3. Many-to-One
  4. Many-to-Many
If you are seeing all this for the first time, I warn you again - it will get worse: think about going for a walk. We will analyze all these relationships using an example, and understand the difference between them.

Horror Example

We will have one project that will have 5 branches: master, where there will be a description of the project, and 1 branch for each DB relationship. Each branch will contain SQL scripts for creating a DB and filling it with test data, plus an Entity class with annotation mapping. There will also be a Hibernate config file for each branch. I will use H2 embeded DB for the project so as not to be distracted by individual aspects of cloud DB or external DB. By following the link, install H2 DB on your vacuum cleaner. I will describe each step in 1 branch, the rest are just the key points. At the end we will summarize. Go. This is a link to the master branch of my project.

One-to-One Relationship

Link to branch here .
  1. We need to connect H2 DB to our project. Here we need to emphasize that we need Ultimate IDEA to work comfortably with DB and other things. If you already have it, then go directly to the DB connection. Go to the Database tab and do as in the screenshot:

    JPA Entities and DB Relationships - 2

    Next we move on to the DB settings. You can enter your data, and even your DBMS; I repeat, I use H2 DB for simplicity.

    JPA Entities and DB Relationships - 3

    Next, let's set up the circuit. This step is optional but recommended if you have multiple schemas in the DB.

    JPA Entities and DB Relationships - 4

    Apply the settings, and in the end we should get something like this:

    JPA Entities and DB Relationships - 5
  2. We created the database and configured access to it from IDEA. Now you need to create tables in it and fill it with some data. For example, I will take two entities: Author and Book. A book may have an author, may have multiple authors, or may not have one. In this example we will create all types of connections. But at this point - One-to-One relationship. Let's create the corresponding script that creates DB Tables :

    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)
    );

    And let's execute it:

    JPA Entities and DB Relationships - 6

    Execution result in the console:

    JPA Entities and DB Relationships - 7

    Result in DB:

    JPA Entities and DB Relationships - 8
  3. Let's look at a diagram of our tables. To do this, RMB on our DB:

    JPA Entities and DB Relationships - 9

    Result:

    JPA Entities and DB Relationships - 10

    On the UML diagram we can see all the primary keys and foreign keys, we also see the connection between our tables.

  4. Let's write a script that fills our DB with test data:

    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);

    I mean, what happens? One-to-One relationship is needed when the entity of one table is related to one entity of another (or not related at all if NOT NULL is removed from BOOK_ID). In our example, one book MUST have one author. No other way.

  5. Now the most interesting thing is how to connect a Java class with DB entities? Very simple. Let's create two classes Book and Author. Using an example, I will analyze class 1 and the key communication fields. Let's take the Author class as an example :

    @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;
    }
Let's figure it out in order:
  1. All fields in the class repeat the attributes of the DB entity.
  2. @Data (from Lombok ) says that for each field a getter and setter will be created, equals, hashcode will be overridden, and a toString method will be generated.
  3. @Entity says that the given class is an entity and is associated with a DB entity.
  4. @DynamicInsert and @DynamicUpdate say that dynamic inserts and updates will be performed in the DB. These are deeper Hibernate settings that will be useful to you so that you have the CORRECT batching.
  5. @Table (name = "AUTHOR") binds the Book class to the DB AUTHOR table.
  6. @Id says that this field is the primary key.
  7. @GeneratedValue (strategy = GenerationType.IDENTITY) – primary key generation strategy.
  8. @Column (name = "ID", nullable = false) associates a field with a DB attribute, and also says that the given DB field cannot be null. This is also useful when generating tables from entities. The reverse process to how we now create our project, this is needed in the test DBs for Unit tests.
  9. @OneToOne says that the given field is a One-to-One relationship field.
  10. @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - a BOOK_ID column will be created, which is unique and not null.
On the reverse side (in the Book class ) we also need to make a One-to-One connection and indicate the field on which the mapping occurs. @OneToOne(mappedBy = "book") - in this example, this is the book field of the Author class. JPA will link them itself. At first glance, it may seem that there is a mess of annotations, but in fact it is very convenient and with experience you will automatically add them without even thinking.
  1. Now let's configure Hibernate. To do this, create a hibernate.cfg.xml file:

    <?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>
Description of properties :
  1. hibernate.dialect is the dialect of the DBMS we have chosen.
  2. hibernate.connection.driver_class — Driver class of our DB.
  3. hibernate.connection.url - utl of our DB. You can take it from the first point where we configured DB.
  4. hibernate.connection.username - DB user name.
  5. hibernate.connection.password — DB user password.
  6. hibernate.hbm2ddl.auto - setting up table generation. If update, then it does not generate if it has already been created, but only updates it.
  7. hibernate.show_sql - whether to show DB queries.
  8. hibernate.format_sql - whether to format DB queries. If not, then they will all be on one line. I recommend turning it on.
  9. hibernate.use_sql_comments - comments DB queries. If this is an Insert, then a comment is written above the request that the request is of the Insert type.
  10. hibernate.generate_statistics - generates logs. I recommend and recommend setting up logging to the maximum. Reading the logs will increase your chances of working correctly with the ORM.
  11. hibernate.jdbc.batch_size — Maximum batch size.
  12. hibernate.jdbc.fetch_size — Maximum fetch size.
  13. hibernate.order_inserts - allows dynamic inserts.
  14. hibernate.order_updates - Allows dynamic updates.
  15. hibernate.jdbc.batch_versioned_data - allows batching. Look at your DBMS: not everyone supports this.
  16. mapping class - classes that are our entities. Everything needs to be listed.
  1. Now our essence must be determined. We can check this in the persistence tab:

    JPA Entities and DB Relationships - 11

    Result:

    JPA Entities and DB Relationships - 12
  2. We also need to configure assign data:

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

    Results: We have done One-to-One mapping. The material is for informational purposes only, details are in the references.

One-to-Many Relationship

Link to branch here . I will no longer post the code in the article, since it is already too long. We look at all the code on GitHub.
  1. As a result of executing the initialization script, we get the following:

    JPA Entities and DB Relationships - 15

    Do you feel the difference with the previous table?

  2. Diagram:

    JPA Entities and DB Relationships - 16

    One-to-Many Relationship - one author can have several books. The left entity corresponds to one or more right ones.

  3. The difference in mapping will be in annotations and fields:

    A field appears in the Author class:

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

    It is already a set, since we can have several books. @OneToMany is talking about attitude type. FetchType.Lazy says that we do not need to load the entire list of books if it is not specified in the request. It should also be said that this field CANNOT be added to toString, otherwise we'll start smoking a StackOverflowError. My beloved Lombok takes care of this:

    @ToString(exclude = "books")

    In the Book class we do Many-to-One feedback:

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

    Here we conclude that One-to-Many is the mirror image of Many-to-One and vice versa. It should be emphasized that Hibernate does not know anything about bidirectional communications. For him, these are two different connections: one in one direction, the other in the opposite direction.

  4. Nothing much has changed in hibernate.cfg.xml .

  5. Persistence:

    JPA Entities and DB Relationships - 17

Many-to-One Relationship

Since Many-to-One is a mirror image of One-to-Many, there will be few differences. Link to branch here .
  1. As a result of executing the initialization script, we get the following result:

    JPA Entities and DB Relationships - 18
  2. Diagram:

    JPA Entities and DB Relationships - 19
  3. The difference in mapping will be in annotations and fields:

    There is no longer a set in the Author class , since it has moved to the Book class.

  4. hibernate.cfg.xml

  5. Persistence:

    JPA Entities and DB Relationships - 20

Many-to-Many Relationship

Let's move on to the most interesting relationship. This relationship, according to all the rules of decency and indecency, is created through an additional table. But this table is not an entity. Interesting, right? Let's take a look at this shit. Link to branch here .
  1. Look at the initialization script , an additional HAS table appears here. We get something like author-has-book.

    As a result of executing the script, we will get the following tables:

    JPA Entities and DB Relationships - 21
  2. Diagram:

    JPA Entities and DB Relationships - 22

    In our example, it turns out that a book can have many authors, and an author can have many books. They may overlap.

  3. Mapping classes will have sets within classes. But as I said, the HAS table is not an entity.

    Author class :

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

    @ManyToMany is a type of relationship.

    @JoinTable - this is exactly what will connect the attribute with an additional HAS table. In it we specify two attributes that will point to the primary keys of two entities.

    Book class :

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

    Here we indicate FetchType and the field we will use to map.

  4. Our hibernate.cfg.xml again remained unchanged (I do not take into account the fact that we created a new DB for each branch).

  5. Persistence:

    JPA Entities and DB Relationships - 23

Debriefing

So, we have superficially examined the types of DB relationships and figured out how to implement them in the ORM model. We wrote a test project that demonstrates all the connections, and figured out how to configure hibernate / jpa. Phew.

useful links

My previous articles: PS There may be errors and shortcomings in the text. PPS The author was smoking something strange while writing this article. Thank you for your attention!
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION