JPA istekli getirme katılmıyor


112

JPA'nın getirme stratejisi tam olarak neyi kontrol eder? Hevesli ve tembel arasında bir fark göremiyorum. Her iki durumda da, JPA / Hazırda Bekletme çoktan bire ilişkilere otomatik olarak katılmaz.

Örnek: Kişinin tek bir adresi vardır. Bir adres birçok kişiye ait olabilir. JPA açıklamalı varlık sınıfları şuna benzer:

@Entity
public class Person {
    @Id
    public Integer id;

    public String name;

    @ManyToOne(fetch=FetchType.LAZY or EAGER)
    public Address address;
}

@Entity
public class Address {
    @Id
    public Integer id;

    public String name;
}

JPA sorgusunu kullanırsam:

select p from Person p where ...

JPA / Hibernate, Kişi tablosundan seçilecek bir SQL sorgusu ve ardından her kişi için ayrı bir adres sorgusu oluşturur :

select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3

Bu, büyük sonuç kümeleri için çok kötü. 1000 kişi varsa, 1001 sorgu oluşturur (1 kişiden ve 1000 adresten farklı). Bunu biliyorum çünkü MySQL'in sorgu günlüğüne bakıyorum. Anladığım kadarıyla, adresin getirme türünü istekli olarak ayarlamak, JPA / Hibernate'in bir birleştirme ile otomatik olarak sorgulamasına neden olur. Ancak, getirme türünden bağımsız olarak, yine de ilişkiler için farklı sorgular üretir.

Yalnızca ona açıkça katılmasını söylediğimde gerçekten katılır:

select p, a from Person p left join p.address a where ...

Burada bir şey mi kaçırıyorum? Artık her sorguyu, çoktan bire ilişkilere katılması için elimden geçirmem gerekiyor. Hibernate'in JPA uygulamasını MySQL ile kullanıyorum.

Düzenleme: JPA sorgularını etkilemeyen görünür ( burada ve burada Hazırda Bekletme SSS bölümüne bakın ) FetchType. Bu yüzden benim durumumda açıkça ona katılmasını söyledim.


5
SSS girişlerine Linkler burada yürümez, kırık bir
n0weak

Yanıtlar:


99

JPA, getirme stratejisini seçmek için eşleme ek açıklamaları hakkında herhangi bir özellik sağlamaz. Genel olarak, ilgili varlıklar aşağıda verilen yollardan herhangi biriyle alınabilir

  • SELECT => kök varlıklar için bir sorgu + her kök varlığın ilgili eşlenmiş varlık / koleksiyonu için bir sorgu = (n + 1) sorgu
  • SUBSELECT => kök varlıklar için bir sorgu + ilgili eşlenmiş varlık için ikinci sorgu / ilk sorguda alınan tüm kök varlıkların koleksiyonu = 2 sorgu
  • JOIN => hem kök varlıkları hem de eşlenen varlık / koleksiyonun tümünü almak için bir sorgu = 1 sorgu

Yani SELECTve JOINiki uç ve SUBSELECTaradaki düşer. Alan modeline göre uygun strateji seçilebilir.

Varsayılan SELECTolarak hem JPA / EclipseLink hem de Hazırda Bekletme tarafından kullanılır. Bu, aşağıdakiler kullanılarak geçersiz kılınabilir:

@Fetch(FetchMode.JOIN) 
@Fetch(FetchMode.SUBSELECT)

Hazırda bekletme modunda. Ayrıca, örneğin parti boyutu SELECTkullanılarak ayarlanabilen modu kullanarak modu açıkça ayarlamaya izin verir .@Fetch(FetchMode.SELECT)@BatchSize(size=10)

EclipseLink'te karşılık gelen ek açıklamalar şunlardır:

@JoinFetch
@BatchFetch

5
Bu ayarlar neden var? JOIN'in neredeyse her zaman kullanılması gerektiğini düşünüyorum. Şimdi tüm eşlemeleri hazırda beklemeye özel ek açıklamalarla işaretlemem gerekiyor.
vbezhenar

4
İlginç ama ne yazık ki @Fetch (FetchMode.JOIN), Criteria'daki gibi JPQL'de benim için (Hibernate 4.2.15) hiç çalışmıyor.
Aphax

3
Hazırda Bekletme ek açıklaması benim için de hiç çalışmıyor, Spring JPA
TheBakker

2
@Aphax Bunun nedeni Hibernate'in JPAQL / Criteria ve em.find () için farklı varsayılan stratejiler kullanması olabilir. Bkz vladmihalcea.com/2013/10/17/... ve referans belgeleri.
Joshua Davis

1
@vbezhenar (ve yorumunu bir süre sonra okurlar): JOIN sorgusu veritabanında kartezyen ürün üretir, böylece kartezyen ürünün hesaplanmasını istediğinizden emin olmalısınız. Getirme birleşimini kullanırsanız, LAZY koysanız bile istekli yükleneceğini unutmayın.
Walfrat

45

"mxc" doğru. fetchTypeSadece belirtir zaman ilişkisi çözülmelidir.

Bir dış birleştirme kullanarak istekli yüklemeyi optimize etmek için eklemeniz gerekir

@Fetch(FetchMode.JOIN)

alanınıza. Bu, hazırda beklemeye özel bir açıklamadır.


6
Bu, JPQL veya Criteria'da Hibernate 4.2.15 ile benim için çalışmıyor.
Aphax

6
@Aphax Sanırım bunun nedeni JPAQL ve Criteria'nın Fetch spesifikasyonuna uymaması. Getirme ek açıklaması yalnızca em.find (), AFAIK için çalışır. Bkz vladmihalcea.com/2013/10/17/... Ayrıca hazırda Docos bakın. Eminim bunun bir yerlerde kapsanmıştır.
Joshua Davis

@JoshuaDavis Demek istediğim, \ @Fetch ek açıklamasının sorgularda herhangi bir JOIN optimizasyonu uygulamadığı, JPQL veya em.find (), Hibernate 5.2'de yeni bir deneme yaptım. + Ve hala aynı
Aphax

38

FetchType özniteliği, ek açıklamalı alanın birincil varlık getirildiğinde hemen getirilip getirilmediğini kontrol eder. Getirme ifadesinin nasıl oluşturulacağını zorunlu olarak belirlemez, gerçek sql uygulaması, toplink / hibernate vb. Kullandığınız sağlayıcıya bağlıdır.

Eğer ayarlarsan fetchType=EAGER Bu, ek açıklamalı alanın varlıktaki diğer alanlarla aynı zamanda değerleriyle doldurulduğu anlamına gelir. Dolayısıyla, bir entitymanager'ı açarsanız, person nesnelerinizi alırsanız ve ardından entitymanager'ı kapatırsanız, daha sonra bir person.address yapmak, tembel bir yük istisnasının atılmasına neden olmaz.

Eğer ayarlarsan fetchType=LAZYAlanı , yalnızca erişildiğinde doldurulur. Entitymanager'ı o zamana kadar kapattıysanız, bir person.address yaparsanız tembel bir yükleme istisnası atılır. Alanı yüklemek için, varlığı em.merge () ile bir entitymangers bağlamına geri koymanız, ardından alan erişimini yapmanız ve ardından entitymanager'ı kapatmanız gerekir.

Müşteri siparişleri için bir koleksiyon içeren bir müşteri sınıfı oluştururken tembel yükleme isteyebilirsiniz. Bir müşteri listesi almak istediğinizde bir müşteri için her siparişi aldıysanız, bu yalnızca müşteri adını ve iletişim bilgilerini aradığınızda pahalı bir veritabanı işlemi olabilir. DB erişimini daha sonraya bırakmak en iyisidir.

Sorunun ikinci kısmı için - optimize edilmiş SQL oluşturmak için nasıl hazırda bekletilir?

Hazırda bekletme, en verimli sorguyu nasıl oluşturacağınıza dair ipuçları sağlamanıza izin vermelidir, ancak tablo yapınızda bir sorun olduğundan şüpheleniyorum. İlişki tablolarda kurulmuş mu? Hibernate, özellikle dizinler vb. Eksikse, basit bir sorgunun bir birleşmeden daha hızlı olacağına karar vermiş olabilir.


20

Şunu deneyin:

select p from Person p left join FETCH p.address a where...

Benim için JPA2 / EclipseLink ile benzer şekilde çalışıyor, ancak bu özellik JPA1'de de mevcut görünüyor :




1

Person sınıfının gömülü bir anahtar sınıfına sahip olması dışında tam olarak bu sorunu yaşadım. Kendi çözüm sorguda VE onlara katılmak oldu kaldır

@Fetch(FetchMode.JOIN)

Gömülü kimlik sınıfım:

@Embeddable
public class MessageRecipientId implements Serializable {

    @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
    @JoinColumn(name="messageId")
    private Message message;
    private String governmentId;

    public MessageRecipientId() {
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public String getGovernmentId() {
        return governmentId;
    }

    public void setGovernmentId(String governmentId) {
        this.governmentId = governmentId;
    }

    public MessageRecipientId(Message message, GovernmentId governmentId) {
        this.message = message;
        this.governmentId = governmentId.getValue();
    }

}

-1

Bende iki şey oluyor.

İlk olarak, adres için ManyToOne'ı kastettiğinizden emin misiniz? Bu, birden fazla kişinin aynı adrese sahip olacağı anlamına gelir. Bunlardan biri için düzenlenmişse, hepsi için de düzenlenecektir. Niyetin bu mu? Zaman adreslerinin% 99'u "özeldir" (yalnızca bir kişiye ait olmaları anlamında).

İkinci olarak, Kişi varlığında başka istekli ilişkiniz var mı? Doğru hatırlıyorsam, Hibernate bir varlık üzerinde sadece bir istekli ilişkiyi idare edebilir, ancak bu muhtemelen güncel olmayan bir bilgidir.

Bunu söylüyorum çünkü bunun nasıl çalışması gerektiğine dair anlayışınız, oturduğum yerden esasen doğru.


Bu, çoktan bire kullanan uydurma bir örnektir. Kişi-adresi en iyi örnek olmayabilir. Kodumda başka istekli getirme türleri görmüyorum.
Steve Kuo

Benim önerim o zaman bunu basit bir örneğe indirgemek, gördüğünüz şeyi yapan ve sonra bunu yayınlamaktır. Modelinizde beklenmedik davranışlara neden olan başka komplikasyonlar olabilir.
cletus

1
Kodu tam olarak yukarıda göründüğü gibi çalıştırdım ve söz konusu davranışı sergiliyor.
Steve Kuo
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.