Chaitanya Chaitanya - 3 months ago 121
Java Question

Difference between @MapKey, @MapKeyColumn and @MapKeyJoinColumn in Hibernate

As per Hibernate documentation, there are multiple annotations available if we want to use Map as an association between our entities. The doc says:


Alternatively the map key is mapped to a dedicated column or columns.
In order to customize the mapping use one of the following
annotations:

@MapKeyColumn if the map key is a basic type. If you don't specify the
column name, the name of the property followed by underscore followed
by KEY is used (for example orders_KEY). @MapKeyEnumerated /
@MapKeyTemporal if the map key type is respectively an enum or a Date.
@MapKeyJoinColumn/@MapKeyJoinColumns if the map key type is another
entity. @AttributeOverride/@AttributeOverrides when the map key is a
embeddable object. Use key. as a prefix for your embeddable object
property names. You can also use @MapKeyClass to define the type of
the key if you don't use generics.


By doing some examples I am able to understand that @MapKey is just used to map the key to a property of target entity and this key is used only for fetching records. @MapKeyColumn is used to map the key to a property of target entity and this key is used to save as well as fetching records. Please let me know if this is correct?

Also please let me know when I need to use @MapKeyJoinColumn/@MapKeyJoinColumns & @MapKeyEnumerated / @MapKeyTemporal

Thanks!

Answer

When you use a Map you always need to associate at least two entities. Let's say we have an Owner entity that relates to the Car entity (Car has a FK to Owner).

So, the Owner will have a Map of Car(s):

Map<X, Car>
  1. The @MapKey will give you the Car's field used to group multiple Car(s) to an Owner:

  2. @MapKeyEnumerated will use an Enum from Car, like WheelDrive:

    @Entity
    public class Owner {
        @Id
        private long id;
    
        @OneToMany(mappedBy="owner")
        @MapKeyEnumerated(EnumType.STRING)
        private Map<WheelDrive, Car> carMap;
    }
    
    @Entity
    public class Car {
        @Id
        private long id;
    
        @ManyToOne
        private Owner owner;
    
        @Column(name = "wheelDrive")
        @Enumerated(EnumType.STRING)
        private WheelDrive wheelDrive;
    
    }
    
    public enum WheelDrive {
        2WD, 
        4WD;             
    }   
    

    This will group cars by their WheelDrive type.

  3. @MapKeyTemporal will use a Date/Calendar field for grouping, like createdOn

    @Entity
    public class Owner {
        @Id
        private long id;
    
        @OneToMany(mappedBy="owner")
        @MapKeyTemporal(TemporalType.TIMESTAMP)
        private Map<Date, Car> carMap;
    }
    
    @Entity
    public class Car {
        @Id
        private long id;
    
        @ManyToOne
        private Owner owner;
    
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name="created_on")
        private Calendar createdOn;         
    }   
    
  4. @MapKeyJoinColumn requires a third entity, like Manufacturer so that you have an association from Owner to Car and car has also an association to a Manufacturer, so that you can group all Owner's Cars by Manufacturer:

    @Entity
    public class Owner {
        @Id
        private long id;
    
        @OneToMany(mappedBy="owner")
        @MapKeyJoinColumn(name="manufacturer_id")
        private Map<Manufacturer, Car> carMap;
    }
    
    @Entity
    public class Car {
        @Id
        private long id;
    
        @ManyToOne
        private Owner owner;
    
        @ManyToOne
        @JoinColumn(name = "manufacturer_id")
        private Manufacturer manufacturer;          
    }
    
    @Entity
    public class Manufacturer {
        @Id
        private long id;
    
        private String name;
    }