Entity relations with Hibernate 4

Last time I introduced annotations instead XML configuration. Now I will dig deeper and show how you can create entity relations and map them to the database.

If you look at the Book entity in the example you might think: “Why store the authors of a book as a String?” and you are right. This makes querying for books by authors almost impossible — or at least not as performant as it could be. And a typo between two entries can make it even worse.

In this article I will take the example further and extract the String of authors into another entity.

The Author entity

Let’s start with a new entity holding Author information. For simplicity it will contain only the name of the author and the list of Books an author created.

@Entity
 @Table(name = "AUTHORS")
 public class Author {
 
    @Id
    private String name;
    @ManyToMany(mappedBy = "authors")
    private final List<Book> books = new ArrayList<>();
 
    private Author() {
    }
 
    public Author(String name) {
        this.name = name;
    }
 
    public String getName() {
        return this.name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<Book> getBooks() {
        return this.books;
    }
 
    @Override
    public String toString() {
        return MessageFormat.format("{0} has written {1} book(s).", this.name, this.books.size());
    }
 }

As you can see, there is a new annotation: @ManyToMany. This tells Hibernate that an Author entity maps to other Book entities. The mappedBy attribute is used to tell Hibernate which entity is the owner side of the relation. This is used mostly in one-to-many or many-to-one relations.

Adding authors to the Book entity

Now let’s change the author String to a list of Authors. It will look quite the same than in the Author entity:

@Entity
 @Table(name = "BOOKS")
 public class Book {
 
    @Id
    private String isbn;
    private String title;
    @ManyToMany
    private final List<Author> authors = new ArrayList<>();
 
    @Temporal(TemporalType.DATE)
    @Column(name = "PUBLISHED_DATE")
    private Date published;
 
    private Book() {
    }
 
    public Book(String isbn, String title, Date published) {
        this.isbn = isbn;
        this.title = title;
        this.published = published;
    }
 
    public String getIsbn() {
        return this.isbn;
    }
 
    public String getTitle() {
        return this.title;
    }
 
    public void setTitle(String title) {
        this.title = title;
    }
 
    public List<Author> getAuthors() {
        return this.authors;
    }
 
    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
 
    public Date getPublished() {
        return this.published;
    }
 
    public void setPublished(Date published) {
        this.published = published;
    }
 
    @Override
    public String toString() {
        final String authorNames = this.authors.stream().map(Author::getName).collect(Collectors.joining(", "));
        return MessageFormat.format("{0} by {1} (ISBN: {2}), published {3}", this.title, authorNames, this.isbn, this.published);
    }
 }

Here I did not add any parameter to the @ManyToMany annotation because the Author is the owner side.

Relation types

Of course the many-to-many relation is not the only one available. You can choose one-to-one, one-to-many or many-to-one too if it fits your needs.

One-to-one relations

This relation type contains for one entity only one of the referenced type — and the same goes for the other entity. In this case you can choose which entity you use to store the reference in. An example would be the relation between Books and ISBN numbers if I would extract the ISBN as a separate entity with some other properties: one book has one ISBN and one ISBN refers to exactly to one book.

One-to-many and many-to-one relations

In this case one entity has many references in other entities. This would be the case if one book could have only one author. In this case the referencing ID would be stored in the BOOKS table because one book references to its author. We use one-to-many in the Author entity and many-to-one in the Book entity.

Many-to-many relations

As you could see in the first example, this relation is a bit more complex. Here we need a separate table to contain the references between books and authors. This is the so called join-table. This table is maintained by Hibernate automatically but you can tell how the table should be named. If you do not choose a name, Hibernate takes the entity names and separates them with an underscore, the owner site-entity is the later name. In this example the join-table is named: BOOKS_AUTHORS.

Changing the main method

Because the entities are changed I have to change the main method of the Main class too.

final Book book = new Book("9781617291999", "Java 8 in Action", new Date());
 session.beginTransaction();
 Arrays.stream("Raoul-Gabriel Urma,Mario Fusco,Alan Mycroft".split(",")).map(name -> new Author(name)).forEach(author -> {
 author.getBooks().add(book);
 book.getAuthors().add(author);
 session.save(author);
 });
 session.save(book);
 session.getTransaction().commit();

As you can see, I use some lambdas to dynamically create the authors. The interesting part is the last statement, the forEach block: I add the book to the list of books of the current author, and then I add the current author to the book’s authors list. This reference is needed to find the books and authors together later in the database (if you query manually or load the dataset with Hibernate).

The result is now a bit different from the last time:

----
 
 Storing 1 books in the database
 Java 8 in Action by Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft (ISBN: 9781617291999), published 2015.06.29. 16:57
 
 ----

If I leave away the statement book.getAuthors().add(author); then the result does not contain the authors’ names:

----
 
 Storing 1 books in the database
 Java 8 in Action by (ISBN: 9781617291999), published 2015.06.29. 16:58
 
 ----

Conclusion

As you could see, there are a bunch of options how you can map your entity relations to the normalized database. Next time I will show you how you can map entity inheritance to your database.

Code Download

You can download chapter specific code from Github Here.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.