Hibernate Tips: How to map a bidirectional many-to-one association

By Thorben Janssen

Association Mapping, Mapping

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question you like me to answer, please leave a comment below.

Question:

My table model contains a many-to-one association. How do I model it with Hibernate so that I can navigate it in both directions?

Solution:

You need to model the association on both entities if you want to be able to navigate it in both directions. Consider this example. A book in an online bookstore can have multiple reviews. In your domain model, the Book entity has a one-to-many association to the Review entity, and the Review entity has a many-to-one relationship to the Book entity.

BidirectionalManyToOne

Let’s begin with the Review entity, which is the owning side of the association in this example. That means that it defines the association and the Book entity just references it. The relationship consists of two mandatory and one optional part. The entity attribute of type Book and the @ManyToOne annotation are required. The attribute models the association, and the annotation declares the type of relationship. The @JoinColumn annotation is optional. It allows you to define the name of the foreign key column. I use it in the following code snippet to set the column name to fk_book.

If you don’t define the name yourself, Hibernate generates a name by combining the name of the association mapping attribute and the name of the primary key attribute of the associated entity. In this example, Hibernate would use book_id as the default column name.

You also need to map the one-to-many association on the Book entity to make it bidirectional. As you can see in the following code snippet, this is done in a similar way as the many-to-one association.

You need an attribute that models the association, which is the List reviews attribute in this example and a @OneToMany annotation. Like in the table model, the bidirectional one-to-many association gets defined on the many side. The table on the many side stores the foreign key and its entity defines the association. It’s similar for the entity mapping. You just need to reference the name of the association attribute of the many side as the value of the mappedBy attribute and Hibernate has all the information it needs.

That’s all you need to do to define a bidirectional many-to-one association. You can now navigate it in both directions in your JPQL or Criteria API queries or on your domain objects.

Bidirectional associations are easy to use in queries, but they also require an additional step when you persist a new entity. You need to update the association on both sides when you add or remove an entity. You can see an example of it in the following code snippet, in which I first create a new Review entity and initialize its association to the Book entity. And after that, I also need to add the new Review entity to the List of reviews on the Book entity.

Updating the associations on both entities is an error-prone task. Therefore, it’s a good practice to provide a helper method that adds another entity to the many side of the association.

Learn More

Bidirectional many-to-one associations are just one way to model relationships between entities. I show you how to model a bi-directional many-to-many association in How to map a bidirectional many-to-many association

 

Hibernate Tips Book


Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

Tags

Association Mapping, Mapping


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Repl​​​​​y

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.

    1. Nothing really bad happens as long as you don’t use the association in your business code after you changed it.
      The entity that you didn’t update contains the wrong data as long as it is in the 1st level cache. If you commit your transaction and load the same entity at a later point in time (into a different 1st level cache), it will contain the correct data.

      Regards,
      Thorben

  1. Thanks, you’re the first one who mentioned that the @JoinColumn in a bidirectional one-to-many assocation is optional.

    The hibernate documentation is confusing, some excerpts:

    2.2.5. Mapping entity associations/relationships
    2.2.5.1. One-to-one
    Bidirectional One-to-one: If no @JoinColumn is declared on the owner side, the defaults apply. A join column(s) will be created in the owner table and its name will be the concatenation of the […]

    2.2.5.2. Many-to-one
    The @JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the […]
    You can also map a many-to-one association through an association table. This association table described by the @JoinTable annotation will […]

    2.2.5.3. Collections
    2.2.5.3.1.1. Bidirectional
    Nothing about @JoinColumn

    2.2.5.3.1.4. Defaults
    Without describing any physical mapping (no @JoinColumn or @JoinTable), a unidirectional one to many with join table is used.

    source: https://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#entity-mapping-association

    The confusion is complete…one time they say the default (without any @JoinColumn) is a join column and then they say the default is a join table!

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}