How to map an association as a java.util.Map


Hi,
I’m Thorben Janssen from thoughts-on-java.org and today I want to show you how to map an association as a java.util.Map. Let’s be honest, a simple List is good
enough in most cases and that’s one of the reasons why it’s the most common representation of a to-many association with JPA and Hibernate. But from time to time, a Map would make
the implementation of your business logic so much easier. So, why not use JPA’s and Hibernate’s
mapping capabilities and map the association to a Map? Only for the relationships that
are used as Maps, of course. The mapping definition requires just a few additional
annotations. The basic mappings are pretty simple. JPA and Hibernate allow you to map a Collection of elements or an entity association to a java.util.Map. The key and value of the Map can be a basic type, an embeddable class or an entity. You might have recognized that Collections are not supported. If you want to use a Collection as the value, you need to use a small workaround
that I will show you at the end of this video. But let’s take a look at some simple mapping
first and discuss the general mapping concepts. Let’s start with a standard mapping definition
of a many-to-many association that uses a List. This diagram shows an example of a many-to-many
association between the Author and the Book. As most mappings, it uses an attribute of
type List to represent the association. If you want to change that to a Map you have
to do 2 things: You need to change the type of the attribute
from List to Map. And you have to define the database column you want to use as a map key. Let’s say you want to use the title column
of the Book table as the map key. The title is a String attribute of the Book. So, it’s a basic type that’s already mapped by your entities and you just need to reference it
in a @MapKey annotation. If you don’t want to use one of the attributes
of the associated entity, you can also use other attributes as keys. Hibernate will persist
then in an additional database column. You can use a basic type with a @MapKeyColumn annotation, an enum with a @MapKeyEnumerated annotation or a java.util.Date or a java.util.Calendar with a @MapKeyTemporal annotation. But back to our example. Let’s jump into
the IDE and apply the required changes. Here you can see the Author entity. As I explained earlier, I changed 2 things: I changed the attribute type to a Map and I added this @MapKey annotation. It tells Hibernate to use the title attribute of the Book entity as the key of the map. That’s all you need to do to define the
mapping. You can now use the map in your domain model to add, retrieve or remove elements
from the association. Here you can see a simple example in which I persist a new Author and a new Book entity. Down here, I call the getBooks method to get the Map that represents the association and add the new Book to it. You can also use the association in a JPQL statement. Here you can see a query that joins the Author and the Book entity in the same way as you would join an association that’s mapped to a List. And you can use the KEY function to reference
the map key in your query. OK, let’s take a look at a mapping of an
embeddable. This mapping is pretty similar. The main difference is that you need to use the @ElementCollection annotation instead of a @ManyToMany annotation. Let’s say you want to persist an author with her business and private address. In your domain model, you might model that with
the 3 classes you can see in the diagram. The Author class becomes an entity and you
can implement the Address class as an embedabble. And for this example, I don’t want to keep
the Addresses in a simple ElementCollection. I want to use a Map with the AddressType enum as key. As in the previous example, you only need
to do 2 things to use a Map instead of a List. You need to change the type of the attribute
from List to Map and you have to define the mapping of the map key. The second step is optional if you only use the enum as a map key and not as an attribute of the embeddable. If you don’t provide any additional information, Hibernate uses a set of defaults to persist the map key in a column of the collection table. I prefer to define the column name and the EnumType Hibernate shall use to persist the enum. The name attribute of the @MapKeyColumn annotation defines the name of the database column and the @MapKeyEnumerated defines how Hibernate shall persist the enum. That’s all you have to do to represent a
collection of embeddables as a java.util.Map. You can now use it in the same way as I showed
you in the previous example. As you’ve seen in the previous examples, JPA and Hibernate don’t support any Collection type as a map value. That’s unfortunate because, in my experience, that is the most common use case. But there is a small and easy workaround for these situations. You just need to introduce a wrapper entity that wraps the List or Map of entities. Let’s extend the example of the Author–Book-association I showed you at the beginning of this video. Most Books get published in different Formats, for example as an ebook and as a paperback. The Book has a unique ISBN for each Format. So, you need 2 database records to persist the ebook and paperback version of a Book. But you can’t persist these 2 records with the mapping from our first example. The 2nd Book entity would replace the 1st one, when you add it to the Map. You can fix that by introducing an additional entity that wraps the List of Book entities. The mapping definition of that entity is very simple. You just need a primary key and a one-to-many association to the Book entity. I also introduced a title for the BookGroup which I want to use as the map key on the Author entity. You can then model the association between
the BookGroup and the Author entity as a Map. That’s all you need to do to model an association as a Map with multiple values per map key. You can now use it in the same way as I showed
you in the first example. Please be aware, that this mapping creates
overhead and redundancy. The BookGroup entity gets mapped to a database table which we didn’t need in the previous examples. And I now use the title of the BookGroup as the key
of the Map. The BookGroup title will be the same as the title of all Books in the group. OK, that’s it for today. If you want to learn more about Hibernate, you should join the free Thoughts on Java Library. It gives you free access to a lot
of member-only content like a cheat for this video and an ebook about the Java 8 support
in the Hibernate 5. I’ll add the link to it to the video description below. And if you like today’s video, please give
it a thumbs up and subscribe below. Bye




Comments
  1. Thanks alot Thorben for this video it saved major part of my use case.
    If there is any enhanced facility for java.util.Map so that overhead and redundancy is will be reduced kindly share.
    Thanks again

  2. Thorben, your hibernate videos, courses, ebooks are awesome…!! I have in fact purchased one of your advanced courses. Have one suggestion though. If you could show the database tables during persistence or removal or any operations, it would really help

  3. The most intelligent anatomy of mapping collections such as maps via JPA. My most sincere congratulations!

Leave a Reply

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