Skip to content

Relationships

Guillaume Le Cousin edited this page Apr 9, 2022 · 2 revisions

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.

One to one

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.

One to many

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.

Many to many

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

Annotations in detail

@ForeignKey

The @ForeignKey annotation can specify the following behavior about the link:

  • optional (default to false) specifies if the foreign key is nullable
  • onForeignDeleted (possible values are SET_TO_NULL and DELETE, default to DELETE) 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 to false) 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.

@ForeignTable

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 to true) specifies if at least one foreign entity must always exist

@JoinTable

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

Cascade save and delete

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

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.