Kompozit anahtarı JPA ve Hazırda Bekletme ile nasıl eşleyebilirim?


205

Bu kodda, bileşik anahtar için bir Java sınıfı nasıl oluşturulur (hazırda bekletme modunda anahtar nasıl birleştirilir):

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;


1
Gerçekten iyi bir örnek: vladmihalcea.com/2016/08/01/…
TecHunter

Yanıtlar:


416

Kompozit tuşunu eşleştirme, kullanabilirsiniz EmbeddedId veyaIdClass ek açıklamalardır. Bu sorunun kesinlikle JPA ile ilgili olmadığını biliyorum, ancak şartname tarafından tanımlanan kurallar da geçerlidir. İşte buradalar:

2.1.4 Birincil Anahtarlar ve Varlık Kimliği

...

Bileşik birincil anahtar, ya tek bir kalıcı alana veya özelliğe ya da aşağıda açıklanan bu tür alanlara ya da özelliklere karşılık gelmelidir. Bileşik birincil anahtarı temsil etmek için birincil anahtar sınıfı tanımlanmalıdır. Bileşik birincil anahtarlar genellikle, veritabanı anahtarı birkaç sütundan oluştuğunda eski veritabanlarından eşleme yapılırken ortaya çıkar. Ve ek açıklamalar bileşik birincil anahtarlar belirtmek için kullanılır. Bkz. Bölüm 9.1.14 ve 9.1.15.EmbeddedIdIdClass

...

Bileşik birincil anahtarlar için aşağıdaki kurallar geçerlidir:

  • Birincil anahtar sınıfı genel olmalı ve genel bir bağımsız değişken yapıcısına sahip olmamalıdır.
  • Özellik tabanlı erişim kullanılıyorsa, birincil anahtar sınıfının özellikleri genel veya korumalı olmalıdır.
  • Birincil anahtar sınıfı olmalıdır serializable.
  • Birincil anahtar sınıfı tanımlamalı equalsve hashCode yöntemler olmalıdır . Bu yöntemler için değer eşitliği semantiği, anahtarın eşlendiği veritabanı türleri için veritabanı eşitliği ile tutarlı olmalıdır.
  • Bileşik bir birincil anahtar katıştırılabilir sınıf olarak gösterilmeli ve eşlenmelidir (bkz. Bölüm 9.1.14, “EmbeddedId Ek Açıklama”) veya varlık sınıfının birden çok alanı veya özelliğiyle temsil edilmeli ve eşlenmelidir (bkz. Bölüm 9.1.15, “IdClass Ek Açıklama”).
  • Bileşik birincil anahtar sınıfı, varlık sınıfının birden çok alanına veya özelliğine eşlenirse, birincil anahtar alanlarının veya birincil anahtar sınıfındaki özelliklerin adları ile varlık sınıfının adları karşılık gelmeli ve türleri aynı olmalıdır.

Bir ile IdClass

Bileşik birincil anahtarın sınıfı aşağıdaki gibi görünebilir (statik bir iç sınıf olabilir):

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
}

Ve varlık:

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

IdClassEk açıklama masa PK birden çok alan eşler.

İle EmbeddedId

Bileşik birincil anahtarın sınıfı aşağıdaki gibi görünebilir (statik bir iç sınıf olabilir):

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

Ve varlık:

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

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

    //...
}

@EmbeddedIdEk açıklama masa PK PK sınıfını eşler.

farklılıklar:

  • Fiziksel model açısından hiçbir fark yoktur
  • @EmbeddedIdbir şekilde anahtarın bileşik bir anahtar olduğunu ve IMO , birleşik pk anlamlı bir varlık olduğunda veya kodunuzda yeniden kullanıldığında daha açık bir şekilde iletişim kurar .
  • @IdClass bazı alan kombinasyonlarının benzersiz olduğunu ancak bunların özel bir anlamı olmadığını belirtmek için yararlıdır .

Ayrıca, sorgu yazma şeklinizi de etkiler (daha fazla veya daha az ayrıntılı hale getirir):

  • ile IdClass

    select t.levelStation from Time t
  • ile EmbeddedId

    select t.timePK.levelStation from Time t

Referanslar

  • JPA 1.0 teknik özellikleri
    • Bölüm 2.1.4 "Birincil Anahtarlar ve Varlık Kimliği"
    • Bölüm 9.1.14 "EmbeddedId Ek Açıklaması"
    • Bölüm 9.1.15 "IdClass Ek Açıklaması"

15
@İd özellikleri olarak ilk birden fazla özellik: Bir hazırda özel bir çözüm de mevcuttur olmadan tanımlayıcı tipi olacaktır (ve IdClass notu kullanın) için bir dış sınıf ilan. Bkz. 5.1.2.1. Hazırda Bekleme kılavuzundaki bileşik tanımlayıcı .
Johan Boberg

Lütfen bu soruya bir göz atabilir misiniz ? Üye alanı idher zaman olduğundan nullve oluşturulan değil çünkü bileşik birincil anahtar ile sorun yaşıyorum : /
displayname

Her iki durumda da nerede oynadıklarını görmekte zorluk çektiğim için lütfen bir alıcı ve ayarlayıcı ile bir örnek koyabilir miyim. Özellikle IdClass örneği. Teşekkürler. Oh ve sütun adları dahil, teşekkürler.
Jeremy

hazırda bekletme özel çözüm kullanımdan kaldırılmıştır.
Nikhil Sahu

Gönderen hazırda Açıklamalar docs , yaklaşık @IdClass: "Bu geriye uyumluluklardan için EJB 2 karanlık çağlardan miras ve size (basitlik uğruna) kullanmamaya tavsiye edildi."
Marco Ferrari

49

Kullanmanız gerekenler @EmbeddedId:

@Entity
class Time {
    @EmbeddedId
    TimeId id;

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

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

@ Thierry-DimitriRoy timeId.levelStation ve timeId.confPathID öğelerini nasıl atayabilirim. Lütfen bir örnek verebilir misiniz?
Duc Tran

@ Thierry-DimitriRoy Birincil sınıf, varlık sınıfının statik bir iç sınıfı olamaz mı?
Nikhil Sahu

Evet, olabilir
Samy Omar

17

Bu makalede açıkladığım gibi, aşağıdaki veritabanı tablolarına sahip olduğunuzu varsayarsak:

resim açıklamasını buraya girin

İlk olarak, @Embeddableholding tutma tanımlayıcısını oluşturmanız gerekir :

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

Bu durumda Employee, bileşik tanımlayıcıyı kullanan varlığı aşağıdakilere ekleyerek eşleştirebiliriz @EmbeddedId:

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

PhoneBir sahip işletme @ManyToOneiçin ilişki Employeeiki vasıtasıyla ana sınıfından bileşik tanımlayıcı referans gerekiyor @JoinColumneşleştirmeleri:

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

Daha fazla ayrıntı için bu makaleye göz atın .


Db şemasından EmployeeId üretebilecek bir araç var mı?
Leon

Hazırda Bekletme Araçlarını deneyin. Bunun için bir Ters mühendislik aracı var.
Vlad Mihalcea

7

Birincil anahtar sınıfı eşittir ve hashCode yöntemlerini tanımlamalıdır

  1. Eşittir uygularken , alt sınıflarla karşılaştırmaya izin vermek için instanceof komutunu kullanmalısınız. Hazırda Bekletme modu bire bir veya birden çoka bir ilişki yüklerse, sınıf için düz sınıf yerine bir proxy'niz olur. Proxy bir alt sınıftır. Sınıf adlarının karşılaştırılması başarısız olur.
    Daha teknik olarak: Liskows İkame Prensibi'ne uymalı ve simetrikliği görmezden gelmelisiniz.
  2. Bir sonraki tuzak, name.equals (that.getName ()) yerine name.equals (that.name) gibi bir şey kullanıyor . Birincisi, bir proxy ise başarısız olur.

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


6

Görünüşe göre bunu sıfırdan yapıyorsun. En azından temel bilgileri otomatikleştirmek için (gömülü kimlikler gibi) Veritabanından Netbeans Varlıkları gibi mevcut tersine mühendislik araçlarını kullanmayı deneyin. Birçok tablonuz varsa, bu büyük bir baş ağrısı olabilir. Tekerleği yeniden icat etmekten kaçınmanızı ve kodlamayı minimum ve en önemli parça olan ne yapmak istediğinizi azaltmak için mümkün olduğunca çok araç kullanmanızı öneririm.


5

Basit bir örnek verelim. Diyelim iki tablo adında demek testve customerorada olarak tarif edilmektedir:

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

Orada tests ve s izini tutan bir tablo daha customer:

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

Tabloda tests_purchasedbirincil anahtarın bileşik bir anahtar olduğunu görebiliyoruz, bu nedenle <composite-id ...>...</composite-id>etiketi hbm.xmleşleme dosyasında kullanacağız . Yani PurchasedTest.hbm.xmlirade şöyle görünecek:

<?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>

Ama burada bitmiyor. Hazırda Bekletme modunda entityClass, id_type_objectbirincil anahtarı kullanarak varlığı bulmak ve yüklemek için session.load ( , ) öğesini kullanırız. Kompozit anahtarlarda, ID nesnesi, aşağıdaki gibi birincil anahtar niteliklerini bildiren ayrı bir ID sınıfı (yukarıdaki durumda bir PurchasedTestIdsınıf) olmalıdır :

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

Önemli olan, aynı zamanda iki işlevi de uygulamamız hashCode()ve equals()Hazırda Bekletme işlevlerine dayandığı için.


2

Eşlemenin bir başka yolu da ConfPath tablosundaki bileşik öğelerin Haritasıdır.

Bu eşleme (ConfPathID, levelStation) üzerindeki bir dizinden yararlanır.

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

haritalama:

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

1

Hbm.xml kullanma

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

Ek Açıklama Kullanma

Kompozit Anahtar Sınıfı

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

Varlık Sınıfı

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

1
Mantıklı değil, birincil anahtara ihtiyacı var
Mazen Embaby

başlığında, birincil olması gerekmeyen kompozit anahtar diyor
Enerccio

lütfen ne sql birincil anahtar
Mazen Embaby
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.