JPA Entities && DB Relationships
Good day, colleagues!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 : 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 .-
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:
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.
Next, let's set up the circuit. This step is optional but recommended if you have multiple schemas in the DB.
Apply the settings, and in the end we should get something like this:
-
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:
Execution result in the console:
Result in DB:
-
Let's look at a diagram of our tables. To do this, RMB on our DB:
Result:
On the UML diagram we can see all the primary keys and foreign keys, we also see the connection between our tables.
-
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.
-
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; }
- All fields in the class repeat the attributes of the DB entity.
- @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.
- @Entity says that the given class is an entity and is associated with a DB entity.
- @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.
- @Table (name = "AUTHOR") binds the Book class to the DB AUTHOR table.
- @Id says that this field is the primary key.
- @GeneratedValue (strategy = GenerationType.IDENTITY) – primary key generation strategy.
- @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.
- @OneToOne says that the given field is a One-to-One relationship field.
- @JoinColumn (name = "BOOK_ID", unique = true, nullable = false) - a BOOK_ID column will be created, which is unique and not null.
-
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>
- hibernate.dialect is the dialect of the DBMS we have chosen.
- hibernate.connection.driver_class — Driver class of our DB.
- hibernate.connection.url - utl of our DB. You can take it from the first point where we configured DB.
- hibernate.connection.username - DB user name.
- hibernate.connection.password — DB user password.
- hibernate.hbm2ddl.auto - setting up table generation. If update, then it does not generate if it has already been created, but only updates it.
- hibernate.show_sql - whether to show DB queries.
- hibernate.format_sql - whether to format DB queries. If not, then they will all be on one line. I recommend turning it on.
- 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.
- 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.
- hibernate.jdbc.batch_size — Maximum batch size.
- hibernate.jdbc.fetch_size — Maximum fetch size.
- hibernate.order_inserts - allows dynamic inserts.
- hibernate.order_updates - Allows dynamic updates.
- hibernate.jdbc.batch_versioned_data - allows batching. Look at your DBMS: not everyone supports this.
- mapping class - classes that are our entities. Everything needs to be listed.
-
Now our essence must be determined. We can check this in the persistence tab:
Result:
-
We also need to configure assign data:
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.-
As a result of executing the initialization script, we get the following:
Do you feel the difference with the previous table?
-
Diagram:
One-to-Many Relationship - one author can have several books. The left entity corresponds to one or more right ones.
-
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.
-
Nothing much has changed in hibernate.cfg.xml .
-
Persistence:
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 .-
As a result of executing the initialization script, we get the following result:
-
Diagram:
-
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.
-
Persistence:
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 .-
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:
-
Diagram:
In our example, it turns out that a book can have many authors, and an author can have many books. They may overlap.
-
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.
-
Our hibernate.cfg.xml again remained unchanged (I do not take into account the fact that we created a new DB for each branch).
-
Persistence:
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
- Actually the project itself
- One-to-One branch
- One-to-Many branch
- Many-to-One branch
- Many-to-Many branch
- Read it
- And read it
GO TO FULL VERSION