30

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!

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Chaitanya
  • 15,403
  • 35
  • 96
  • 137

2 Answers2

61

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>

@MapKey

The @MapKey will give you the Car's property used to group a Car to its Owner. For instance, if we have a vin (Vehicle Identification Number) property in Car, we could use it as the carMap key:

@Entity
public class Owner {
    @Id
    private long id;

    @OneToMany(mappedBy="owner")
    @MapKey(name = "vin")
    private Map<String, Car> carMap;
}

@Entity
public class Car {
    @Id
    private long id;

    @ManyToOne
    private Owner owner;

    private String vin;

}

@MapKeyEnumerated

The @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.

@MapKeyTemporal

The @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;         
}

@MapKeyJoinColumn

The @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;
}
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • Hi Vlad, if possible can you please take a look at my other post - http://stackoverflow.com/questions/25664452/components-as-composite-identifiers-in-hibernate/ Thanks a lot for your support. – Chaitanya Sep 04 '14 at 16:16
  • I'll take a look tonight – Vlad Mihalcea Sep 04 '14 at 17:09
  • I'd have a similar question. For some reason `@MapKey` does not give me the expected result. Could you take a look at this too: http://stackoverflow.com/questions/31235462/transitive-mapping-to-child-entity ? ^^ – Stefan Falk Jul 05 '15 at 22:45
  • @VladMihalcea Currently I'm working on migration from `hibernate 3.3.2` to `hibernate 4.3.11`. How to migrate `@MapKey` cause MapKey cannot be resolved to a type in `hiberante4` – macemers Aug 28 '15 at 01:42
  • Could u compare @MapKeyType as well, please? I found little information on the Internet – macemers Aug 28 '15 at 03:55
  • It's quite urgent, so could you plz take a look at this similar question:http://stackoverflow.com/questions/32307329/hibernate-4-what-should-replace-deprecated-mapkey-to-map-a-map-collection-whi – macemers Aug 31 '15 at 09:30
  • 1
    I'm on vacation. I'll see when I get back. – Vlad Mihalcea Aug 31 '15 at 10:00
  • There is a colon at the end of paragraph 1; was there once an example, because there is no code in the answer that shows the difference between MapKey and MapKeyColumn, which is I think the OP's main question, and yet the question is considered as answered. – JL_SO Sep 17 '18 at 03:37
  • Now, it does. Check out the example. – Vlad Mihalcea Sep 17 '18 at 05:23
0

Here's a working example of using @MapKey with @OneToMany with a composite @IdClass. It's obviously not the only way to accomplish the objective here, but I felt this was the most maintainable.

@Entity
@Table(name = "template_categories")
@IdClass(TemplateCategoryId.class)
public class TemplateCategory implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    long orgId;
    @Id
    long templateId;

    @OneToMany(targetEntity = TemplateEntry.class)
    @JoinColumns( {
        @JoinColumn(name = "orgId",  referencedColumnName = "orgId"),
        @JoinColumn(name = "templateId",  referencedColumnName = "templateId")
        }
    )
    @MapKey(name="key")
    private Map<String, TemplateEntry> keyMap;

source code: https://github.com/in-the-keyhole/jpa-entity-map-examples/blob/master/src/main/java/com/example/demo/mapkey/entity/TemplateCategory.java

Ryan
  • 1