Şimdi Ara

ORM(Hibernate) relationship performans hakkında

Daha Fazla
Bu Konudaki Kullanıcılar: Daha Az
2 Misafir (1 Mobil) - 1 Masaüstü1 Mobil
5 sn
5
Cevap
0
Favori
394
Tıklama
Daha Fazla
İstatistik
  • Konu İstatistikleri Yükleniyor
0 oy
Öne Çıkar
Sayfa: 1
Giriş
Mesaj
  • Merhaba Arkadaşlar, Hibernate ile ilgili kafama takılan bir soru var. İncelediğim projelerde oneToMany,ManyToOne gibi çoklu ilişkisel yapı bulunan projelerde kayıt işlemlerini aşağıdaki gibi yapıldığını gördüm.

    Örneğin elimizde şöyle bi yapı var (basit olması açısından doğaçlıyorum modelleri) :
    Okul, Ogrenci,OgrenimDerecesi

    ilişkileri de aşağıdaki şekildeki gibi

    ORM(Hibernate) relationship performans hakkında


    veri katmanını da Springdata'nın üstlendiğini varsayarak ogrenciyi kayit veya update eden Service sınıfı methodu da aşağıdaki gibi:

    @Override
    @Transactional
    public OgrenciCommand saveOgrenciCommand(OgrenciCommand command) {
    Optional<Okul> okulOptional = okulRepository.findById(command.getOkulId());

    if(!okulOptional.isPresent()){

    //todo toss error if not found!
    log.error("Okul not found for id: " + command.getOkulId());
    return new OgrenciCommand();
    } else {
    Okul okul = okulOptional.get();

    Optional<Ogrenci> OgrenciOptional = okul
    .getOgrenciler()
    .stream()
    .filter(Ogrenci -> Ogrenci.getId().equals(command.getId()))
    .findFirst();

    if(OgrenciOptional.isPresent()){
    Ogrenci OgrenciFound = OgrenciOptional.get();
    OgrenciFound.setIsim(command.getIsim());
    OgrenciFound.setSoyisim(command.getSoyisim());
    OgrenciFound.setOgrenimDerecesi(ogrenimDerecesiRepository
    .findById(command.getOgrenimDerecesi().getId())
    .orElseThrow(() -> new RuntimeException("Ogrenim derecesi bulunamadi")));
    } else {
    //add new Ogrenci
    okul.addOgrenci(OgrenciCommandToOgrenci.convert(command));
    }

    Okul savedOkul = okulRepository.save(okul);

    //todo check for fail
    return OgrenciToOgrenciCommand.convert(savedOkul.getOgrenciler().stream()
    .filter(okulOgrenciler -> okulOgrenciler.getId().equals(command.getId()))
    .findFirst()
    .get());
    }

    }



    kodu özet geçersek :

    - ogrenciyi kayit etmek istedigimiz okul varsa onu çekiyoruz.
    - bu okula ait olan ogrencileri cekiyoruz
    - bu cektigimiz ogrencilerin icinden kayit etmek istegimiz ogrenci varmi diye kontrol ediyoruz varsa verilerimizi bunun ustune editliyoruz, yoksa yeni olusturup uzerinde ekliyoruz
    - bu ogrenciyi kayit ettrirmek istedigim ogrenim derecesi varsa onuda çekip ogrenciye ekliyoruz.
    - en sonda okulu kayit ediyoruz

    yani benim anlatmak istediğim şey bir nesneyi kayıt ederken neden onla ilişkisi olan diğer nesneleri de çekip tekrar üzerinde ekleyip kayıt ediyoruz ??
    SQL de direk foreign key belirterek kolay bir şekilde eklediğimiz şeyi Hibernate ile niçin bu kadar fazladan işlem yaparak hallediyoruz ?
    bu büyük bir performans kaybı değilmidir ?



    < Bu mesaj bu kişi tarafından değiştirildi Charizard_11 -- 27 Kasım 2017; 4:59:46 >







  • Cascade tanimlarina gore bu sekilde yapiliyor, model objeleri hbm de nasil tanimlanmis ona bakmak gerek, sadece bu koddan o anlasilmaz.



    Bir de bu islem sonucunda hibernate in nasil bir sql uretecegini bilmiyoruz, yani relation i biliyoruz ama cascade tanimini bilmedigimiz icin son sql ciktisinin ne kadar optimize olacagi konusunda yine kesin bir sey diyemeyiz.



    Performanstan kastettigin bu yazilan kodun database tarafindaki performansi sanirim, cunku metodu transactional tanimlamissiniz hibernate commit yapacak bu metod return edince. Bu durumda dedigim gibi model objelerin tanimlarina bakmak gerek nasil calisacagi hakkinda yorum yapabilmek icin.

    < Bu ileti mobil sürüm kullanılarak atıldı >
  • Mephalay M kullanıcısına yanıt
    Cascade typler bu sekilde

    public class  okul{ 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "okul")
    private Set<Ogrenci> ogrenciler = new HashSet<>();
    }



    public class Ogrenci{ 
    @ManyToOne
    private Okul okul;

    @OneToOne(fetch = FetchType.EAGER)
    private OgrenimDerecesi derece;
    }


    --SQL çıktılarına baktığım zaman ilgili Okul(bütün kolonlarıyla birlikte) ve bu okul ve dereceye ait ogrenciler(derece tablosuyla join edilmiş bir şekilde) select edilmiş.(bir ogrenciyi kayit etmek için db den neden bu kadar veri çektik? )
    -- ve biz sonda okul nesnesini save etmemize rağmen ilginç bir şekilde(en azından bana göre:)) sadece uzerine ekledigimiz ogrenciyi update lemiş.Bunu nasıl yaptığınıda anlamadım.
    -- bir de derece ile okul arasında ki ilişki many one şeklinde davranmasına rağman oneToOne olarak belirtilmiş ve hiçbir sorun olmadan ManyToOne olarak çalışıyor bu da şaşırttı ayrıca




  • genel olarak ORM/Hibernate ile performans konusu cok tartisilan boyutta. Nihayetinde eger size lazim olan sey gercek bir performans ise ORM isine hic girmemeniz belki da daha saglikli olur.

    son uc soruya bildigim kadar cevap vereyim;
    1. Hibernate genel olarak zaten herseyi select icine koyuyor, siz programlama yaparken o kaydin bir sutunu uzerinde islem yapacaksaniz o anda islemi gerceklestiriyor. Bu kadar cok veriyi almaya gerek yok gibi bir durum varsa Criteria sinifini kullanmanizi tavsiye edenler var:https://stackoverflow.com/questions/950718/how-to-return-an-entity-with-chosen-columns-using-criteria
    Ayrica Hibernate icinde dogrudan SQL kullanarak da sorgular yapabilirsiniz:https://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch13.html
    Bir de propertileri lazy olarak da tanimlayabiliyorsunuz ki her zaman lazy oluyor, belki baska yerde problem cikarabilir yine de bakmakta fayda var:
    https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-fetching-lazyproperties

    2. save ile ilgili soruya proje ustunde deneyerek bakmak lazim, su an aklima bir sey gelmedi.

    3. iliskiler yanlis tanimlanmis da olsa her iliski iki yonlu olmasi gerekmiyor, yani bir taraftan lazim oldugu kadar iliski tanimlasaniz belki yeterlidir, o yuzden yanlis tanimladiginiz taraftan belki veri cekmiyor/kullanmiyorsaniz o kisim hic islemediginden sorun olmamistir. Kod anlaminda bakinca bir butunluk ortaya cikarmamis olabilir yani. Bunu da kodda net gostermemissiniz ya da ben goremedim. O yuzden biraz havada kalan bir cevap oldu




  • Referans aldığım proje budur hocam : spring5-recipe-app

    Verdiğim modellerin proje üzerindeki orjinal karşılıklarıda bu şekilde :
    Okul -> Recipe
    Ogrenci -> Ingredient
    OkulDercesi -> UnitOfMeasure

    kullanım senaryosu şu şekilde :
    Recipe bir yemek tarafini temsil ediyor.
    Ingredient te bu yemekte kullanılan malzemeleri temsil ediyor.(tuz, karabiber,su vs).
    UnitOfMeasure de bu ingredient'in kullanim miktarı diyeyim(çay kaşığı,yemek kaşığı,su bardağı vs gibi)

    ingredient update işleminin view tarafındaki activiteside şu şekilde :

    -> ilgili recipe'e ye ait ingredient listesinden bir ingredient secilir ve açılan update sayfasından değiştirilmek istenen bilgiler güncellenir (unitOfMeasure da dropdown dan seçiliuor) ve update gerçekleşir.


    update işleminde verdiğim linkteki kodun 63 üncü satırındaki method çalışıyor.

    işlemden sonraki sql çıktılarınıda şu şekilde net olarak vereyim :

    Hibernate: select recipe0_.id as id1_3_0_, recipe0_.cook_time as cook_tim2_3_0_, recipe0_.description as descript3_3_0_, recipe0_.difficulty as difficul4_3_0_, recipe0_.directions as directio5_3_0_, recipe0_.image as image6_3_0_, recipe0_.notes_id as notes_i11_3_0_, recipe0_.prep_time as prep_tim7_3_0_, recipe0_.serving as serving8_3_0_, recipe0_.source as source9_3_0_, recipe0_.url as url10_3_0_, notes1_.id as id1_2_1_, notes1_.recipe_id as recipe_i3_2_1_, notes1_.recipe_notes as recipe_n2_2_1_, recipe2_.id as id1_3_2_, recipe2_.cook_time as cook_tim2_3_2_, recipe2_.description as descript3_3_2_, recipe2_.difficulty as difficul4_3_2_, recipe2_.directions as directio5_3_2_, recipe2_.image as image6_3_2_, recipe2_.notes_id as notes_i11_3_2_, recipe2_.prep_time as prep_tim7_3_2_, recipe2_.serving as serving8_3_2_, recipe2_.source as source9_3_2_, recipe2_.url as url10_3_2_ from recipe recipe0_ left outer join notes notes1_ on recipe0_.notes_id=notes1_.id left outer join recipe recipe2_ on notes1_.recipe_id=recipe2_.id where recipe0_.id=?
    Hibernate: select ingredient0_.recipe_id as recipe_i4_1_0_, ingredient0_.id as id1_1_0_, ingredient0_.id as id1_1_1_, ingredient0_.amount as amount2_1_1_, ingredient0_.description as descript3_1_1_, ingredient0_.recipe_id as recipe_i4_1_1_, ingredient0_.uom_id as uom_id5_1_1_, unitofmeas1_.id as id1_5_2_, unitofmeas1_.description as descript2_5_2_ from ingredient ingredient0_ left outer join unit_of_measure unitofmeas1_ on ingredient0_.uom_id=unitofmeas1_.id where ingredient0_.recipe_id=?
    Hibernate: update ingredient set amount=?, description=?, recipe_id=?, uom_id=? where id=?



    yani böyle bir seneryo için yapılan işlemler biraz saçma gelmişti bana, sanırım dediğiniz gibi bir yapı kurgulamak gerekicek.

    bu tarz ilişkilerin save update işlemleri için net belirlenmiş best practice ler bulamadım maalesef



    < Bu mesaj bu kişi tarafından değiştirildi Charizard_11 -- 4 Aralık 2017; 16:59:16 >




  • 
Sayfa: 1
- x
Bildirim
mesajınız kopyalandı (ctrl+v) yapıştırmak istediğiniz yere yapıştırabilirsiniz.