JPA和Hibernate中@MapKey、@MapKeyColumn和@MapKeyJoinColumn的区别

30
根据Hibernate文档,如果我们想要在实体之间使用Map作为关联,则有多个可用注释。文档说:
或者将映射键映射到专用列。为了自定义映射,请使用以下注释之一:
- @MapKeyColumn 如果映射键是基本类型。如果您不指定列名,则使用属性名称后跟下划线再跟KEY的名称(例如orders_KEY)。 - @MapKeyEnumerated/@MapKeyTemporal 如果映射键类型分别为枚举或日期。 - @MapKeyJoinColumn/@MapKeyJoinColumns 如果映射键类型是另一个实体。 - @AttributeOverride/@AttributeOverrides 当映射键是可嵌入对象时。使用key.作为您的可嵌入对象属性名称的前缀。如果您不使用泛型,则还可以使用@MapKeyClass定义键的类型。
通过一些示例,我理解@MapKey仅用于将键映射到目标实体的属性,并且此键仅用于获取记录。而@MapKeyColumn用于将键映射到目标实体的属性,并且此键用于保存和获取记录。如果我的理解正确,请告诉我。

另外,请告诉我何时需要使用@MapKeyJoinColumn / @MapKeyJoinColumns和@MapKeyEnumerated / @MapKeyTemporal。

谢谢!

2个回答

62
当你使用一个 Map 时,你总是需要关联至少两个实体。假设我们有一个与 Car 实体相关的 Owner 实体(Car 有一个 FK 到 Owner),那么 Owner 将拥有一组 Car(s)Map
Map<X, Car>

@MapKey

@MapKey可以给出用于将Car分组到其Owner的属性。例如,如果我们在Car中有一个vin(车辆识别码)属性,我们可以将其用作carMap的键:

@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

@MapKeyEnumerated将使用Car枚举类中的枚举值,例如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;             
}

这将按照车辆的WheelDrive类型进行分组。

@MapKeyTemporal

@MapKeyTemporal将使用Date / Calendar字段进行分组,例如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

@MapKeyJoinColumn需要第三个实体,例如Manufacturer,以使您从OwnerCar具有关联,并且车辆也与Manufacturer有关联,从而可以通过Manufacturer对所有Owner'sCars进行分组:

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

嗨,弗拉德,如果可能的话,你能否请看一下我的另一篇帖子 - http://stackoverflow.com/questions/25664452/components-as-composite-identifiers-in-hibernate/ 非常感谢您的支持。 - Chaitanya
我有一个类似的问题。由于某种原因,@MapKey没有给我期望的结果。你能否也看一下这个链接:https://dev59.com/1Izda4cB1Zd3GeqPprCU?^^ - Stefan Falk
@VladMihalcea 目前我正在从 hibernate 3.3.2 迁移到 hibernate 4.3.11。如何迁移 @MapKey,因为在 hiberante4 中无法解析 MapKey 类型。 - macemers
请问您能否也比较一下@MapKeyType呢?我在互联网上找到的信息很少。 - macemers
1
我正在度假。回来后再见。 - Vlad Mihalcea
显示剩余4条评论

0
这是一个使用@MapKey和@OneToMany与复合@IdClass的工作示例。显然,这不是实现目标的唯一方法,但我觉得这是最可维护的方法。
@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;

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接