230

このコードでは、複合キーの Java クラスを生成する方法 (休止状態でキーを複合する方法):

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;
4

8 に答える 8

455

複合キーをマップするには、EmbeddedId またはIdClass注釈を使用できます。この質問は厳密にはJPAに関するものではありませんが、仕様で定義されたルールも適用されます。だからここにある:

2.1.4 主キーとエンティティ ID

...

複合主キーは、以下で説明するように、単一の永続フィールドまたはプロパティ、またはそのようなフィールドまたはプロパティのセットのいずれかに対応する必要があります。複合主キーを表すには、主キー クラスを定義する必要があります。通常、複合主キーは、データベース キーが複数の列で構成されている場合に、レガシー データベースからマッピングするときに発生します。および 注釈は、複合主キーを示すために使用されます。EmbeddedIdIdClassセクション 9.1.14 および 9.1.15 を参照してください。

...

複合主キーには次の規則が適用されます。

  • 主キー クラスはパブリックである必要があり、引数なしのパブリック コンストラクターが必要です。
  • プロパティ ベースのアクセスを使用する場合、主キー クラスのプロパティは public または protected である必要があります。
  • 主キー クラスは である必要がありますserializable
  • 主キー クラスはメソッドを定義する必要がありequalsますhashCodeこれらのメソッドの値の等価性のセマンティクスは、キーがマップされるデータベース タイプのデータベースの等価性と一致している必要があります。
  • 複合主キーは、埋め込み可能なクラスとして表されてマップされるか (セクション 9.1.14「EmbeddedId アノテーション」を参照)、エンティティ クラスの複数のフィールドまたはプロパティに表されてマップされる必要があります (セクション 9.1.15「IdClass を参照してください)」。注釈」)。
  • 複合主キー クラスがエンティティ クラスの複数のフィールドまたはプロパティにマップされている場合、主キー クラスの主キー フィールドまたはプロパティの名前と、エンティティ クラスの主キー フィールドまたはプロパティの名前が一致し、それらの型が同じである必要があります。

IdClass

複合主キーのクラスは次のようになります (静的内部クラスの可能性があります)。

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

    select t.levelStation from Time t
    
  • EmbeddedId

    select t.timePK.levelStation from Time t
    

参考文献

  • JPA 1.0 仕様
    • 2.1.4項「主キーとエンティティID」
    • 9.1.14項「EmbeddedId注釈」
    • 9.1.15項「IdClass注釈」
于 2010-08-27T21:56:29.927 に答える
53

使用する必要があります@EmbeddedId

@Entity
class Time {
    @EmbeddedId
    TimeId id;

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

@Embeddable
class TimeId implements Serializable {
    Integer levelStation;
    Integer confPathID;
}
于 2010-08-27T14:24:21.400 に答える
7

主キー クラスは equals および hashCode メソッドを定義する必要があります

  1. equals を実装するときは、サブクラスとの比較を可能にするためにinstanceof を使用する必要があります。Hibernate が 1 対 1 または多対 1 のリレーションを遅延ロードする場合、プレーン クラスの代わりにクラスのプロキシが作成されます。プロキシはサブクラスです。クラス名の比較は失敗します。
    より技術的には、Liskows Substitution Principle に従い、対称性を無視する必要があります。
  2. 次の落とし穴は、 name.equals(that.getName())の代わりにname.equals (that.name)のようなものを使用することです。それがプロキシの場合、最初のものは失敗します。

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

于 2013-01-18T16:28:46.457 に答える
6

これを最初からやっているようです。Netbeans Entities from Database などの利用可能なリバース エンジニアリング ツールを使用して、少なくとも基本的な部分 (埋め込み ID など) を自動化してみてください。多くのテーブルがある場合、これは大きな頭痛の種になる可能性があります。車輪の再発明を避け、利用可能なツールをできるだけ多く使用して、コーディングを最小限かつ最も重要な部分、つまり意図することだけに減らすことをお勧めします。

于 2010-08-27T20:00:35.697 に答える
2

もう 1 つのオプションは、ConfPath テーブル内の複合要素のマップとしてマップすることです。

ただし、このマッピングには (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>
于 2010-08-29T11:33:27.290 に答える