4. Relationships
1. Mapping Relations
As we mentioned in the introduction, we are going to analyze how to map the different types of relationships. Before we start discussing the cardinality of relationships, we have to consider the meaning of these relationships, and we are going to review the concept of directionality of relationships.
- Unidirectional \(\rightarrow\) We will say that a relationship is unidirectional when we will access the related object (component) from another object (owner). For example, if we mount an engine in a car, the logical thing is that the owner is the car, and from it, we will obtain the engine. In this case, inside the Car object, an Engine object will appear, and the Engine will not have an existence of its own.
- Bi directional \(\rightarrow\) They are relations in which the related elements usually have the same weight or entity. For example, a Group of an institute and a Tutor. From a group if it makes sense to know the tutor, and we can also from a teacher (the tutor), access the group he tutors. In this case, within the Group object we have a reference to the Tutor object and vice versa.
Warning
In this type of references, as can be deduced, there is an intrinsic recursion. So, when we handle this type of bidirectional relationships, be very careful not to cause loops, since even something as simple as printing can cause our program to crash and the well-known StackOverflowException
From now, we could study all representations with JPA
2. One to One Relationships
For the explanation of the examples, we will see the design and implementation in the database of each case and how it looks in Hibernate. For this example we are going to represent a 1:1 relationship between Group and Teacher, whereas you can see that a Group has a Tutor, and a Tutor only could tutoring one Group.
Firstly, the class that is pointed by the foreign key. Easy-peasy, because we do not need nothing to do.
| Java | |
|---|---|
And now, the class that contains the foreign key. Here we need to mark that a Group need a Teacher as tutor. Let's see:
tutor of Profesor class, and:
@OneToOne(cascade = CascadeType.ALL)we mark this relation as 1:1. Moreover, we say thecascadeattribute, the most important thing. Cascading is the way say that when we perform some action on the target entity (Grupo), the same action will be applied to the associated entity (Profesor). Let's review the most relevant options:CascadeType.ALLpropagates all operations. The same operation that we do in the target will be done it in the associated.CascadeType.PERSISTpropagates only persist operation on database (save).CascadeType.SAVE_UPDATEis from Hibernate, not JPA, and propagate thesaveOrUpdate()method. Is very similar to persist.CascadeType.REMOVEorCascadeType.DELETEpropagates deletion of entities. Be very careful with this option to avoid lose data.- In the
@JoinColumnwe set: - name of the column in our database
- name of the column referenced in target entity Profesor
unique=trueto ensure that the relation is 1:1 (a teacher can not be related with any other Group)- [optional] to set the foreign key constraint name, in case you want to rename or remove in future operations.
You can find more info at baeldung
2.1. One to One bidirectional
If we want to store in Profesor the groups that he or she is tutoring, we need to add a reference to the Group. Due to we have done the foreign key in Group, it will be very easy:
With mappedBy="tutor" we are saying that in Grupo class exists a field called tutor with the whole information about the relation. Notice that no extra fields will beb added in Profesor, because information about the relation is in Group table.
3. One to Many
For this explanation we will start from the following model, in which a Book has an Author who has written it, and an Author may have written several Books. In the relational schema, the relationship is from idAuthor in Books, which is foreign key to the Author table (ID).
First, we can decide who is the owner of the relation. It really does not mind, but in several designs it is very clear, for instance between Student and Email, who obviously the owner is Student. Normally should be the class with many cardinality the owner. Let's go with the sample.
In this sample, a Book has an (unique) author. We implement it storing a reference to an Author object, called elAutor inside our Book. We have to write the relation information in this field:
- We must mark this field as
@ManyToOne, because Book is at the many's side of the relation (remember that an Author can write several Books) - The foreign key will be annotated with
@JoinColumnlabel, with several attributes: - Since
elAutoris the initial point of the foreign key, who point to theAuthortable, we need to say the name of the primary key in it class. This attribute is optional, but is a good option to improve our code. - Optional, we can name the constraint of the foreign key, with a well-structured name, with the
foreignKeyattribute
Author class is on the one side, and it means that it can write many Books. For this reason we store all the books it has written in a Set of books. The annotations will be:
- As one Author can write many books, we mark the Set of books as
@OneToMany. As we have written the relation specification in Book, we could say that the relation is mapped inelAutorfield insideBookclass, withmappedBy="elAutor"easily.
Decissió
Instead of storing books in a Set, it can be stored in List. The main difference is to answer this question: is it important the order?. If you respond yes, you must use a List. It the response is no you must use a Set.
Espai
The 1:N that we have explained is bidirectional relation. It means that from an Author we can get all the Books he/she has written, and from a Book we can get the Author.

You could find several pages and books who explain unidirectional 1:N relations. It means that with this kind of implementation we only can travel in one direction. In this case we must store only inside a Book who is the author, because Book is the owner. We must remove the Set of books in author to get an unidirectional relation.
3.1. Fetch type
This attribute usually appears when we have in a 1:N or N:M relationship in a class that has a collection of related classes (can also be specified with a 1:1 but is less common). When Hibernate loads an object, it will load its general attributes (name, nationality, etc...), but what about the Books he has written, loads them or not?
DateType.EAGER\(\rightarrow\) Literally translated as anxious (ansioso). We can't wait, and when Author is loaded, Hibernate will resolve the relationship and load all the books with all the internal data for each book. We have all the data at the moment.DateType.LAZY\(\rightarrow\) Literally as lazy (vago), but more representative as lazy loading. If we load the Author, Hibernate only loads the Author's own attributes, without loading its Books. When we try to access their books from our program, Hibernate is activated and loads them. That is, in LAZY mode, the data is loaded when is needed.
What we will do?*
Which is better or worse? The answer is not simple, since both have pros and cons:
- In
EAGERonly one access is made, while inLAZYtwo accesses or more. - In the
EAGERall data is loaded, even if they are not needed, in theLAZYonly what is needed is loaded.
The programmer must assess and balance the amount of information required at a given time and the cost of access to the database.
4. Many to Many
In this section we will finish with the last type of relationships that we can find in the E/R model, which are many-to-many relationships. It can appear other relationships with higher cardinalities, such as ternary relationships, but as was studied in the first year, all of them can be modeled with binary transformations.
Within binary relationships, we can find two possibilities:
- Relationships that simply indicate the relationship (for example, that a character may or may not carry a certain type of weapon in a role-playing game) or
- Relationships that, apart from indicating it, add new attributes. For instance, an actor participates in a film in a type of role: main, secondary, etc.
In the relational model, both cases ended up being modeled as a new table (with or without the attribute). If we find ourselves in the second case, a new table with the attributes it possesses must be modeled with a class, so the N:M relationship between two tables there will be two one-to-many relationships 1:N and N:1 (actor-performance and performance-film) We will focus on the first case, since we are already prepared to solve the second one.
Improving
For the second part, this tutorial explains how to do it N_M with attributes. Is very recommmended to implement it a sample.
We are going to model the typical case of a Teacher who teaches several Modules, which can be taught by several teachers. The scheme looks like below:
As we can see, the typical central table of the N:M relationship remains. As previously mentioned, the Teaching table will not exist in the OO model, since it only serves to relate the elements.
The Module and Teacher classes are as follows (only the part related to the relationship is shown) choosing in this case Teacher as the owner of the relationship:
| Java | |
|---|---|
This is the most complicated specification, let's go:
- In both classes the mapping is
@ManyToMany - In both cases we indicate how we handle the cascade operations (
cascade) and the loading of the related objects of the other class (fetch) - In the owner class (
Professor) aSet<Module>is mapped with the relationship will start from my current classProfessor\(\rightarrow\) teaching \(\rightarrow\)Modulo(the base type of the Set) - With
@JoinTableIt is indicated that the relationship links to a table withDocencianame, where: - It will be linked (
joincolumns), and the join is with@JoinColumn:- Starting from the
idProfesorfield inside theDocenciatable - Finishing on the
Profesorprimary key, - The FK is named
foreignKey = @ForeignKey(name = "FK_DOC_PROF" ).
- Starting from the
- Pay attention that name inside @JoinTable are names in the table Docencia (existing only on the database).
- It is mapped from the
Docenciatable to theModulosource entity inversely (from the point to the origin of the arrow): - This is achieved with
inverseJoinColumns:- linking from the
idModulefield (@JoinColumn). - We also name the FK.
- linking from the
- In the related class (
Modulo), which is not the owner, we simply indicate that the owner isProfesor, by means ofmappedBy="losModulos".
A sample code will be like this:
and the hibernate output will be something like:
After all the tables were created, Hibernate creates the foreign keys, and then insert the recors. Firstly, Profesor and Modulo, and finally the relation in Docencia table.