-
Notifications
You must be signed in to change notification settings - Fork 12
Relationships
3 kinds of relationships can be done:
- One-to-one: In One-To-One relationship, one item can belong to only one other item. It means each row of one entity is referred to one and only one row of another entity. The link may be optional (one item can be linked to zero or one other item) or mandatory (one item is always linked to one other item). For example a UserAccount entity and a Person entity can be linked with a one-to-one relationship, meaning a user account will always be linked to a person, and a person can have only one account.
- One-to-many: In this relationship each row of one entity is referenced to many (possibly zero) child records in other entity. For example a single Publisher may be references by many books but a book can reference only one Publisher.
- Many-to-many: In this relationship each item A can be referenced by many (possibly zero) other items B, but one item B can also be referenced by many (possible zero) other items A. For example a book can be referenced by several co-authors, and an author can be referenced by several books.
In a relational database, relationships are done using foreign keys. Similarly, on Java entities the @ForeignKey
annotation represents a foreign key in the database.
To represent the relationship on the other side, the @ForeignTable
annotation is used. For many-to-many relationships the @JoinTable
annotation is used.
Examples for each type of relation is described below in the sub-sections.
Let's consider the example with UserAccount and Person:
+--------+ +-------------+
| Person | 1 <---> 0..1 | UserAccount |
+--------+ +-------------+
A Person may or may not have a UserAccount, however a UserAccount is always linked to a single Person.
To represent this relationship in database, the UserAccount table contains a foreign key to the table Person. So in Java it is the same:
@Table
public class UserAccount {
@ForeignKey(optional = false, onForeignDeleted = OnForeignDeleted.DELETE, cascadeDelete = false)
private Person person;
}
We can note the following:
- We use the annotation
@ForeignKey
to represent the foreign key in database. - The type of the attribute is not the type of the foreign key but directly the type of the foreign entity (Person in our case).
- We specify that the link is not optional for a UserAccount, in other words the foreign key is not nullable.
- We specify that if the Person is deleted, we want to automatically delete the linked UserAccount.
- But in the other way we don't want to cascade delete the Person when the UserAccount is deleted.
The Person entity will be like this:
@Table
public class Person {
@Id
private Long id;
@ForeignTable(joinKey = "person", optional = true)
private UserAccount account;
}
The ForeignTable
annotation is not necessary for the link to work, its goal is to indicates on the Person entity that it may be linked to a UserAccount, and to access to this link easily. We can note in the annotation that:
- We indicate in
joinKey
the name of the attribute containing the@ForeignKey
annotation to use for this link. - We indicate that the link is optional, meaning the account attribute may be null.
The one-to-many relationship is very similar to the one-to-one. Let's consider the example with Book and Publisher:
+-----------+ +------+
| Publisher | 1 ---> n | Book |
+-----------+ +------+
A Publisher may publish several books, but a single Book is always published by a single Publisher.
To represent this relationship in database, the Book table contains a foreign key to the table Publisher. So in Java it is the same:
@Table
public class Book {
@Id @GeneratedValue
private Long id;
@ForeignKey
private Publisher publisher;
}
This is exactly the same as the one-to-one relationship.
Now, let's see the Publisher entity:
@Table
public class Publisher {
@Id @GeneratedValue
private Long id;
@ForeignTable(joinKey = "publisher")
private Set<Book> books;
}
The only difference here is the @ForeignTable
attribute is a collection instead of a single object.
The many-to-many relationship is a bit more complex, let's consider our example with Book and Author:
+--------+ +------+
| Author | n ---> n | Book |
+--------+ +------+
One author may have written several books, and a book may be written by several (co-)authors.
To represent this relationship in database, we must introduce an additional join table that will link the two tables together. This join table will contain a foreign key to the author, and a foreign key to the book, each row representing a link. In Java we can use the @JoinTable
annotation, without introducing an additional entity, this additional entity will be automatically generated but this is completely transparent:
@Table
public class Book {
@Id
private Long id;
@JoinTable(joinProperty = "books")
private Set<Author> authors;
}
@Table
public class Author {
@Id
private Long id;
@JoinTable(joinProperty = "authors")
private Set<Book> books;
}
The @ForeignKey
annotation can specify the following behavior about the link:
-
optional
(default tofalse
) specifies if the foreign key is nullable -
onForeignDeleted
(possible values areSET_TO_NULL
andDELETE
, default toDELETE
) specifies what to do when the foreign entity is deleted: either to set the foreign key to null, or delete the entities containing the foreign key. -
cascadeDelete
(default tofalse
) specifies if the foreign entity (linked by this foreign key) must be deleted when the entity containing the foreign key is deleted. It can be used together with the Spring annotation@Column
to specify the column name.
The @ForeignTable
annotation can specify the following behavior about the link:
-
joinKey
must be set to specify which attribute (that must be a@ForeignKey
) of the linked entity to use for the link -
optional
(default totrue
) specifies if at least one foreign entity must always exist
The @JoinTable
annotation provides additional information about how to make a many to many relationship:
-
tableName
is the intermediate table to use, if not specified it will be automatically generated -
columnName
is the column of the intermediate table to use as a foreign key to the entity containing the@JoinTable
annotation. If not specified it will be automatically generated -
joinProperty
is the name of the attribute on the linked entity containing the@JoinTable
annotation specifying the other side of the link
When saving or deleting an entity, the cascade behavior will analyze linked entities to save or delete them as needed.
For example for a one to many link between a publisher and books, if you add new books and remove some books on the publisher entity, saving the publisher entity will automatically insert the new books and delete the removed books.
Next steps are how to use Lazy loading to get linked entities on demand, and Joins to load linked entities using a single SELECT query.