it-swarm.com.de

Wie kann ich einen zusammengesetzten Schlüssel mit Hibernate zuordnen?

In diesem Code wird beschrieben, wie eine Java-Klasse für den zusammengesetzten Schlüssel generiert wird (wie der Schlüssel im Ruhezustand zusammengesetzt wird):

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;
176
kaaf

Um einen zusammengesetzten Schlüssel zuzuordnen, können Sie die EmbeddedIdoder die IdClass-Anmerkungen verwenden. Ich weiß, dass es sich bei dieser Frage nicht unbedingt um JPA handelt, sondern es gelten auch die in der Spezifikation definierten Regeln. Also hier sind sie:

2.1.4 Primärschlüssel und Objektidentität

...

Ein zusammengesetzter Primärschlüssel muss entsprechen entweder einem einzelnen beständiges Feld oder Eigenschaft oder an eine Festlegen von Feldern oder Eigenschaften wie nachstehend beschrieben. Eine Primärschlüsselklasse muss definiert werden, um eine .__ darzustellen. zusammengesetzter Primärschlüssel. Zusammengesetzt Primärschlüssel entstehen normalerweise, wenn Zuordnung aus älteren Datenbanken, wenn die Datenbankschlüssel besteht aus mehreren Säulen. Die Annotationen EmbeddedId und IdClass werden verwendet, um Zusammengesetzte Primärschlüssel zu bezeichnen. Siehe Abschnitte 9.1.14 und 9.1.15.

...

Die folgenden Regeln gelten für zusammengesetzte Primärschlüssel:

  • Die Primärschlüsselklasse muss public sein und über einen öffentlichen no-arg -Konstruktor verfügen.
  • Wenn ein auf Eigenschaften basierender Zugriff verwendet wird, werden die Eigenschaften des Primärschlüssels Klasse muss öffentlich oder geschützt sein.
  • Die Primärschlüsselklasse muss serializable sein. 
  • Die Primärschlüsselklasse Muss die Methoden equals und hashCode Definieren. Die Semantik von value Gleichheit für diese Methoden muss .__ sein. im Einklang mit der Datenbankgleichheit für die Datenbanktypen, auf die die Schlüssel ist zugeordnet.
  • Ein zusammengesetzter Primärschlüssel muss entweder als .__ dargestellt und zugeordnet werden. einbettbare Klasse (siehe Abschnitt 9.1.14, „EmbeddedId-Anmerkung“) oder muss .__ sein. dargestellt und mehreren zugeordnet Felder oder Eigenschaften der Entität Klasse (siehe Abschnitt 9.1.15, „IdClass Annotation“).
  • Wenn die zusammengesetzte Primärschlüsselklasse mehreren Feldern oder .__ zugeordnet ist. Eigenschaften der Entitätsklasse, der Namen von Primärschlüsselfeldern oder Eigenschaften in der Primärschlüsselklasse und die der Entitätsklasse müssen korrespondieren und ihre Typen müssen die .__ sein. gleich.

Mit einem IdClass

Die Klasse für den zusammengesetzten Primärschlüssel könnte folgendermaßen aussehen (könnte eine statische innere Klasse sein):

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
}

Und die Entität:

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

Die Annotation IdClass ordnet der Tabelle PK mehrere Felder zu. 

Mit EmbeddedId

Die Klasse für den zusammengesetzten Primärschlüssel könnte folgendermaßen aussehen (könnte eine statische innere Klasse sein):

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

Und die Entität:

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

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

    //...
}

Die @EmbeddedId-Annotation ordnet eine PK-Klasse der Tabellen-PK zu.

Unterschiede:

  • Aus Sicht des physikalischen Modells gibt es keine Unterschiede
  • @EmbeddedId kommuniziert irgendwie klarer, dass der Schlüssel ein zusammengesetzter Schlüssel ist, und IMO ist sinnvoll , wenn der kombinierte pk entweder eine sinnvolle Entität selbst ist oder in Ihrem Code wiederverwendet wird.
  • @IdClassist nützlich, um anzugeben, dass einige Kombinationen von Feldern eindeutig sind, diese jedoch keine besondere Bedeutung haben.

Sie wirken sich auch auf die Art und Weise aus, wie Sie Abfragen schreiben (und machen sie mehr oder weniger ausführlich):

  • mit IdClass

    select t.levelStation from Time t
    
  • mit EmbeddedId

    select t.timePK.levelStation from Time t
    

Verweise

  • JPA 1.0-Spezifikation
    • Abschnitt 2.1.4 "Primärschlüssel und Objektidentität"
    • Abschnitt 9.1.14 "EmbeddedId-Anmerkung"
    • Abschnitt 9.1.15 "IdClass-Anmerkung" 
384
Pascal Thivent

Sie müssen @EmbeddedId verwenden:

@Entity
class Time {
    @EmbeddedId
    TimeId id;

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

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

Wie ich in diesem Artikel erklärt habe, vorausgesetzt, Sie haben die folgenden Datenbanktabellen:

 enter image description here

Zuerst müssen Sie den @Embeddable erstellen, der die zusammengesetzte ID enthält:

@Embeddable
public class EmployeeId implements Serializable {

    @Column(name = "company_id")
    private Long companyId;

    @Column(name = "employee_number")
    private Long employeeNumber;

    public EmployeeId() {
    }

    public EmployeeId(Long companyId, Long employeeId) {
        this.companyId = companyId;
        this.employeeNumber = employeeId;
    }

    public Long getCompanyId() {
        return companyId;
    }

    public Long getEmployeeNumber() {
        return employeeNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof EmployeeId)) return false;
        EmployeeId that = (EmployeeId) o;
        return Objects.equals(getCompanyId(), that.getCompanyId()) &&
                Objects.equals(getEmployeeNumber(), that.getEmployeeNumber());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCompanyId(), getEmployeeNumber());
    }
}

Damit können wir die Employee-Entität, die den zusammengesetzten Bezeichner verwendet, durch Annotieren mit @EmbeddedId zuordnen:

@Entity(name = "Employee")
@Table(name = "employee")
public class Employee {

    @EmbeddedId
    private EmployeeId id;

    private String name;

    public EmployeeId getId() {
        return id;
    }

    public void setId(EmployeeId id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Die Phone-Entität, die eine @ManyToOne-Zuordnung zu Employee hat, muss über zwei @JoinColumnmappings auf den zusammengesetzten Bezeichner der übergeordneten Klasse verweisen:

@Entity(name = "Phone")
@Table(name = "phone")
public class Phone {

    @Id
    @Column(name = "`number`")
    private String number;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(
            name = "company_id",
            referencedColumnName = "company_id"),
        @JoinColumn(
            name = "employee_number",
            referencedColumnName = "employee_number")
    })
    private Employee employee;

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}

Weitere Informationen finden Sie in diesem Artikel .

12
Vlad Mihalcea

Sieht aus, als würden Sie dies von Grund auf tun. Verwenden Sie verfügbare Reverse Engineering-Tools wie Netbeans-Entitäten aus der Datenbank, um zumindest die Grundlagen zu automatisieren (wie eingebettete IDs). Dies kann zu großen Kopfschmerzen führen, wenn Sie viele Tabellen haben. Ich empfehle, das Rad nicht neu zu erfinden und so viele Tools wie möglich einzusetzen, um die Codierung auf ein Minimum und den wichtigsten Teil zu reduzieren.

6
javydreamercsw

Die Primärschlüsselklasse muss die Methoden equals und hashCode definieren

  1. Bei der Implementierung von equals sollten Sie instance von verwenden, um den Vergleich mit Unterklassen zu ermöglichen. Wenn Hibernate Lazy eine 1: 1-Beziehung oder eine Beziehung von 1: 1 zu 1 lädt, haben Sie anstelle der einfachen Klasse einen Proxy für die Klasse. Ein Proxy ist eine Unterklasse. Der Vergleich der Klassennamen würde fehlschlagen.
    Technischer: Sie sollten dem Liskows-Substitutionsprinzip folgen und die Symmetrie ignorieren. 
  2. Die nächste Falle verwendet etwas wie name.equals (that.name) anstelle von name.equals (that.getName ()). Der erste wird fehlschlagen, wenn dies ein Proxy ist. 

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

6

Nehmen wir ein einfaches Beispiel. Angenommen, zwei Tabellen mit den Namen test und customer werden dort beschrieben:

create table test(
  test_id int(11) not null auto_increment,
  primary key(test_id));

create table customer(
  customer_id int(11) not null auto_increment,
  name varchar(50) not null,
  primary key(customer_id));

Es gibt noch eine weitere Tabelle, die tests und customer verfolgt:

create table tests_purchased(
  customer_id int(11) not null,
  test_id int(11) not null,
  created_date datetime not null,
  primary key(customer_id, test_id));

Wir können sehen, dass der Primärschlüssel in der Tabelle tests_purchased ein zusammengesetzter Schlüssel ist. Daher verwenden wir das <composite-id ...>...</composite-id>-Tag in der hbm.xml-Zuordnungsdatei. Der PurchasedTest.hbm.xml wird also so aussehen:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
  <class name="entities.PurchasedTest" table="tests_purchased">

    <composite-id name="purchasedTestId">
      <key-property name="testId" column="TEST_ID" />
      <key-property name="customerId" column="CUSTOMER_ID" />
    </composite-id>

    <property name="purchaseDate" type="timestamp">
      <column name="created_date" />
    </property>

  </class>
</hibernate-mapping>

Aber hier endet es nicht. In Hibernate verwenden wir session.load (entityClass, id_type_object), um die Entität mithilfe des Primärschlüssels zu suchen und zu laden. Bei zusammengesetzten Schlüsseln sollte das ID-Objekt eine separate ID-Klasse sein (in diesem Fall eine PurchasedTestId-Klasse) die die Primärschlüsselattribute wie unten deklariert:

import Java.io.Serializable;

public class PurchasedTestId implements Serializable {
  private Long testId;
  private Long customerId;

  // an easy initializing constructor
  public PurchasedTestId(Long testId, Long customerId) {
    this.testId = testId;
    this.customerId = customerId;
  }

  public Long getTestId() {
    return testId;
  }

  public void setTestId(Long testId) {
    this.testId = testId;
  }

  public Long getCustomerId() {
    return customerId;
  }

  public void setCustomerId(Long customerId) {
    this.customerId = customerId;
  }

  @Override
  public boolean equals(Object arg0) {
    if(arg0 == null) return false;
    if(!(arg0 instanceof PurchasedTestId)) return false;
    PurchasedTestId arg1 = (PurchasedTestId) arg0;
    return (this.testId.longValue() == arg1.getTestId().longValue()) &&
           (this.customerId.longValue() == arg1.getCustomerId().longValue());
  }

  @Override
  public int hashCode() {
    int hsCode;
    hsCode = testId.hashCode();
    hsCode = 19 * hsCode+ customerId.hashCode();
    return hsCode;
  }
}

Ein wichtiger Punkt ist, dass wir auch die beiden Funktionen hashCode() und equals() implementieren, da der Ruhezustand von ihnen abhängt.

4
dinesh kandpal

Eine weitere Option ist die Zuordnung einer Karte aus zusammengesetzten Elementen in der ConfPath-Tabelle.

Diese Zuordnung würde jedoch von einem Index für (ConfPathID, levelStation) profitieren.

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

Kartierung:

<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>
2
Maurice Perry

Verwenden von hbm.xml

    <composite-id>

        <!--<key-many-to-one name="productId" class="databaselayer.users.UserDB" column="user_name"/>-->
        <key-property name="productId" column="PRODUCT_Product_ID" type="int"/>
        <key-property name="categoryId" column="categories_id" type="int" />
    </composite-id>  

Annotation verwenden

Zusammengesetzte Schlüsselklasse

public  class PK implements Serializable{
    private int PRODUCT_Product_ID ;    
    private int categories_id ;

    public PK(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId;
        this.categories_id = categoryId;
    }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    private PK() { }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }

        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }

        PK pk = (PK) o;
        return Objects.equals(PRODUCT_Product_ID, pk.PRODUCT_Product_ID ) &&
                Objects.equals(categories_id, pk.categories_id );
    }

    @Override
    public int hashCode() {
        return Objects.hash(PRODUCT_Product_ID, categories_id );
    }
}

Entity-Klasse

@Entity(name = "product_category")
@IdClass( PK.class )
public  class ProductCategory implements Serializable {
    @Id    
    private int PRODUCT_Product_ID ;   

    @Id 
    private int categories_id ;

    public ProductCategory(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId ;
        this.categories_id = categoryId;
    }

    public ProductCategory() { }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    public void setId(PK id) {
        this.PRODUCT_Product_ID = id.getPRODUCT_Product_ID();
        this.categories_id = id.getCategories_id();
    }

    public PK getId() {
        return new PK(
            PRODUCT_Product_ID,
            categories_id
        );
    }    
}
0
Mazen Embaby