在这段代码中,如何为组合键生成一个Java类(如何在hibernate中组合键):

create table Time (
     levelStation int(15) not null,
     src varchar(100) not null,
     dst varchar(100) not null,
     distance int(15) not null,
     price int(15) not null,
     confPathID int(15) not null,
     constraint ConfPath_fk foreign key(confPathID) references ConfPath(confPathID),
     primary key (levelStation, confPathID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当前回答

另一种选择是将is映射为ConfPath表中复合元素的map。

不过,这种映射将受益于(ConfPathID,levelStation)上的索引。

public class ConfPath {
    private Map<Long,Time> timeForLevelStation = new HashMap<Long,Time>();

    public Time getTime(long levelStation) {
        return timeForLevelStation.get(levelStation);
    }

    public void putTime(long levelStation, Time newValue) {
        timeForLevelStation.put(levelStation, newValue);
    }
}

public class Time {
    String src;
    String dst;
    long distance;
    long price;

    public long getDistance() {
        return distance;
    }

    public void setDistance(long distance) {
        this.distance = distance;
    }

    public String getDst() {
        return dst;
    }

    public void setDst(String dst) {
        this.dst = dst;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }
}

映射:

<class name="ConfPath" table="ConfPath">
    <id column="ID" name="id">
        <generator class="native"/>
    </id>
    <map cascade="all-delete-orphan" name="values" table="example"
            lazy="extra">
        <key column="ConfPathID"/>
        <map-key type="long" column="levelStation"/>
        <composite-element class="Time">
            <property name="src" column="src" type="string" length="100"/>
            <property name="dst" column="dst" type="string" length="100"/>
            <property name="distance" column="distance"/>
            <property name="price" column="price"/>
        </composite-element>
    </map>
</class>

其他回答

你需要使用@EmbeddedId:

@Entity
class Time {
    @EmbeddedId
    TimeId id;

    String src;
    String dst;
    Integer distance;
    Integer price;
}

@Embeddable
class TimeId implements Serializable {
    Integer levelStation;
    Integer confPathID;
}

主键类必须定义equals和hashCode方法

When implementing equals you should use instanceof to allow comparing with subclasses. If Hibernate lazy loads a one to one or many to one relation, you will have a proxy for the class instead of the plain class. A proxy is a subclass. Comparing the class names would fail. More technically: You should follow the Liskows Substitution Principle and ignore symmetricity. The next pitfall is using something like name.equals(that.name) instead of name.equals(that.getName()). The first will fail, if that is a proxy.

http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html

另一种选择是将is映射为ConfPath表中复合元素的map。

不过,这种映射将受益于(ConfPathID,levelStation)上的索引。

public class ConfPath {
    private Map<Long,Time> timeForLevelStation = new HashMap<Long,Time>();

    public Time getTime(long levelStation) {
        return timeForLevelStation.get(levelStation);
    }

    public void putTime(long levelStation, Time newValue) {
        timeForLevelStation.put(levelStation, newValue);
    }
}

public class Time {
    String src;
    String dst;
    long distance;
    long price;

    public long getDistance() {
        return distance;
    }

    public void setDistance(long distance) {
        this.distance = distance;
    }

    public String getDst() {
        return dst;
    }

    public void setDst(String dst) {
        this.dst = dst;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }
}

映射:

<class name="ConfPath" table="ConfPath">
    <id column="ID" name="id">
        <generator class="native"/>
    </id>
    <map cascade="all-delete-orphan" name="values" table="example"
            lazy="extra">
        <key column="ConfPathID"/>
        <map-key type="long" column="levelStation"/>
        <composite-element class="Time">
            <property name="src" column="src" type="string" length="100"/>
            <property name="dst" column="dst" type="string" length="100"/>
            <property name="distance" column="distance"/>
            <property name="price" column="price"/>
        </composite-element>
    </map>
</class>

看来你是从零开始的。尝试使用可用的逆向工程工具,如Netbeans Entities from Database,至少实现基本的自动化(如嵌入式id)。如果你有很多桌子,这可能会成为一个非常令人头痛的问题。我建议避免重新发明轮子,并使用尽可能多的可用工具,将编码减少到最小和最重要的部分,即您想要做的部分。

要映射复合键,可以使用EmbeddedId或IdClass注释。我知道这个问题不是严格地关于JPA,但是规范定义的规则也适用。所以他们在这里:

2.1.4 Primary Keys and Entity Identity ... A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId and IdClass annotations are used to denote composite primary keys. See sections 9.1.14 and 9.1.15. ... The following rules apply for composite primary keys: The primary key class must be public and must have a public no-arg constructor. If property-based access is used, the properties of the primary key class must be public or protected. The primary key class must be serializable. The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped. A composite primary key must either be represented and mapped as an embeddable class (see Section 9.1.14, “EmbeddedId Annotation”) or must be represented and mapped to multiple fields or properties of the entity class (see Section 9.1.15, “IdClass Annotation”). If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key fields or properties in the primary key class and those of the entity class must correspond and their types must be the same.

使用id类

复合主键的类看起来像这样(可以是一个静态内部类):

public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

以及实体:

@Entity
@IdClass(TimePK.class)
class Time implements Serializable {
    @Id
    private Integer levelStation;
    @Id
    private Integer confPathID;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    // getters, setters
}

IdClass注释将多个字段映射到表PK。

与EmbeddedId

复合主键的类看起来像这样(可以是一个静态内部类):

@Embeddable
public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

以及实体:

@Entity
class Time implements Serializable {
    @EmbeddedId
    private TimePK timePK;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    //...
}

@EmbeddedId注释将一个PK类映射到表PK。

差异:

从物理模型的角度来看,两者没有区别 @EmbeddedId以某种方式更清楚地传达了键是复合键,当组合的pk本身是有意义的实体或在代码中重用时,IMO是有意义的。 @IdClass用于指定字段的某些组合是唯一的,但这些组合没有特殊含义。

它们还会影响你编写查询的方式(使它们变得更详细或更少):

与IdClass 从时间t开始选择t. levelstation 与EmbeddedId select t. timepk . levelstation from Time t

参考文献

JPA 1.0规范 第2.1.4节主键和实体标识 EmbeddedId注释 章节9.1.15 IdClass注释