Hibernate Tips: Easiest way to manage bi-directional associations
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
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 for a future Hibernate Tip, please leave a comment below.
Question:
Updating both ends of a bidirectional association is an error-prone task. What’s the best way to implement it in a reusable way so that you don’t need to do it in the business code?
Solution:
There are 2 general ways to implement the management of bidirectional associations. You can:
- Add methods to your entities that manage the associations.
- Activate bytecode enhancement so that Hibernate manages the association itself.
You can use the first option with all JPA implementations. The second one is specific to Hibernate and enhances the bytecode of your entity classes at build time. Both of them make the management of bidirectional associations easier by automatically updating the other end of the association.
EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Author a = new Author(); a.setFirstName("Thorben"); a.setLastName("Janssen"); em.persist(a); Book b = new Book(); b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems"); b.setAuthor(a); em.persist(b); em.getTransaction().commit(); em.close();
Let’s take a closer look at both options for the following simple model. One Author has written one or more Books, and each Book was written by one Author.
Additional Methods
The general idea of this approach is simple: Instead of writing the code required to manage the association everywhere you add or remove an element, you just move the code into a method on each entity.
I did that for the setAuthor method on my Book entity. As you can see, it first updates the association on the provided Author entity before it updates its author attribute.
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private int version; private String title; @ManyToOne private Author author; public Author getAuthor() { return author; } public void setAuthor(Author author) { // update association on Author entity if (author != null) { author.getBooks().add(this); } else if (this.author != null) { this.author.getBooks().remove(this); } this.author = author; } ... }
The implementation of a similar method on the Author entity is even easier. The setAuthor method on the Book entity already performs all the required steps to manage the association. So, you just need to call it on the provided Book entity.
@Entity public class Author { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Version private int version; private String firstName; private String lastName; @OneToMany(mappedBy = "author") private Set<Book> books = new HashSet<Book>(); public Set getBooks() { return this.books; } public void setBooks(final Set books) { this.books = books; } public void addBook(final Book book) { book.setAuthor(this); } ... }
Bytecode Enhancement
With Hibernate’s bytecode enhancement feature, you can manage bidirectional associations even easier. You just need to add a Maven or Gradle plugin to your build process, which will add the required code the class files of your entities.
The following configuration adds the hibernate-enhance-maven-plugin to your maven build and generates the required code to manage bidirectional associations.
<project> ... <build> <plugins> <plugin> <groupId>org.hibernate.orm.tooling</groupId> <artifactId>hibernate-enhance-maven-plugin</artifactId> <version>${hibernate.version}</version> <executions> <execution> <configuration> <failOnError>true</failOnError> <enableAssociationManagement>true</enableAssociationManagement> </configuration> <goals> <goal>enhance</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
That’s all you need to do. During your next Maven build, the plugin will add additional code into the setter methods of the to-one sides of your bi-directional associations. This code updates the other end of the association so that you don’t need to do it yourself.
Learn more:
If you enjoed this post, you might also be interested in the following posts about association mappings with JPA and Hibernate:
- How to Choose the Most Efficient Data Type for To-Many Associations – Bag vs. List vs. Set
- Ultimate Guide – Association Mappings with JPA and Hibernate
- How to map an association as a java.util.Map
- Best Practices for Many-To-One and One-To-Many Association Mappings
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.