JPA/Hibernate'ı Bırakın
(stemlaur.com)Özet
JPA/Hibernate, artık Java kodunda SQL yazmaya gerek bırakmadığı gerekçesiyle yaygın olarak kullanılan bir framework haline geldi. Ancak ben, yeni projelerde bunun kullanılmaması gerektiğini savunmak istiyorum.
Nedenler
Aşırı uzun resmî dokümantasyon
Resmî dokümantasyon PDF'e dönüştürüldüğünde tam 406 sayfa ediyor; bu da Yüzüklerin Efendisi'nden (231 sayfa) ve SQL standart dokümanından (288 sayfa) daha uzun. Veritabanı sorgularını öğrenmek için yüksek lisans yapmaya gerek yok.
Değiştirilebilirlik
- Bir entity belirli bir öğeye ihtiyaç duysa bile, parametresiz bir constructor zorunlu tutulur.
- Entity sınıfına
final,abstractanahtar sözcüklerini ekleyerek kalıtımı engelleyemezsiniz. - Reflection/Introspection, OOP'nin kapsülleme ilkesini yok sayar.
- Birisi kötü niyetli kod enjekte ederek verileri tamamen silebilir.
Lazy loading ve cache
@Lazyanotasyonu, yeni başlayanlar için olabilecek en kötü tekniklerden biridir. Ancak domain tasarımı Hibernate ile uyuşmadığında ya da sorgu yazamadığınızda bundan kaçınmak zordur.- Cache mekanizmasını anlamak zordur. Üstelik anlasanız bile, sorgu sonucunu değil entity'nin kendisini cache'e koymanız gerekir.
Bellek-veritabanı senkronizasyonu (Flush)
Flush adlı teknik, bellekte tutulan nesneler ile veritabanını senkronize eder. Bu da iki soruna yol açar.
- Flush devreye girdiğinde bellekteki düzenlemeler tamamlanmış sayıldığı için, Hibernate dışındaki başka bir kalıcılık aracı kullanmak fiilen imkânsız hale gelir,
- Flush sırasında çakışma olursa, kodla ilgisiz Stack Trace hataları ortaya çıkabilir.
Tek bir tablodan belirli sütunları almak
Bir entity içinden yalnızca tek bir sütuna erişmek istiyorsanız, SQL yaklaşımı şu kadar basittir.
select url from image
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';
Ama Hibernate, varsayılan olarak entity'nin tüm sütunlarını getirir. Bunu önlemek için karmaşık süreçlerden geçmek gerekir.
Sütun constraint'lerini tanımlama
Bir sütun üzerindeki constraint'leri tanımlamak için aşağıdaki gibi birden çok anotasyon eklemek gerekir.
...
@NotNull
@NotEmpty
@Email
private String email;
...
Bu yaklaşım şu sorunlara yol açar.
- Bu koşullar için unit test yazamazsınız.
- Flush işlemi sırasında bu süreci anlamak için artık çok geçtir.
- Ortaya çıkabilecek exception'lar geneldir ve pek işe yaramaz.
- İş kurallarını yalnızca teknik kurallar olarak ele alır.
Stratejiyle ilgili sorunlar
- Framework güncellemeleri berbat olabilir, geriye dönük uyumluluğu göz ardı eder ve sizi koşulsuz bağımlı hale getirir. Bunu üreten şirket, tekelleşmek için kendi framework'ünü kullanmanın doğal olduğunu düşünmeye başlar. Bu kısır döngü durdurulmalıdır.
- Proof of Concept elde etmek için ille de framework'e dayanmak, yalnızca bakış açınızı daraltır; özellikle de JPA/Hibernate söz konusuysa. Buna zerre kadar tolerans gösterilmemelidir.
Ne yapmalı?
SQL kullanın
Yalnızca SQL ile her şey yapılabilir. Tüm programcılar bunu bilir, sorgular sezgiseldir ve framework gerekmez.
Yönetici Hibernate kullanın diyorsa?
İstifa edin ya da kodu framework'ten ayıracak bir çalışma yapın.
Zaten kullanıyorsanız...
- Varsayılan constructor ve setter'ların erişim düzeyini
publicyapmayın. - SQL'in ürettiği kimlikler yerine UUID gibi string'ler kullanın.
XXXRepositoryyerineXXXDaoadını kullanın.@SequenceGeneratoranotasyonunu kullanmayın.- Domain sınıfları ile DAO sınıflarını
interfaceile ayırın. - Çokluk ilişkilerini (
@OneToManyvb.) kullanmayın; hatta mümkünse entity mapping'den kaçının.
Sonuç
JPA/Hibernate'ı bırakın.
- İş problemlerini çözmek için daha kısa ve daha iyi dokümanlar var.
- Tasarıma gereğinden fazla hızlı yöntemlerle saplanıp kalmayın.
- Kodunuzu devralacak bir sonraki geliştiriciye karşı cömert olun.
Siz ne kullanıyorsunuz?
- JPA/Hibernate
- Başka bir ORM teknolojisi
22 yorum
JPA/Hibernate’ı bırakma görüşüne katılmıyorum.
"Çok uzun resmi dokümantasyon" kısmı
SQL de ilk öğrenildiğinde zordur. Karmaşık join’leri, alt sorguları, prosedür fonksiyonlarını vb. eksiksiz anlamak kolay mı?
JPA’de başlangıçta yalnızca temel kavramları anlayarak başlamak da yeterlidir. Daha derin konulara ihtiyaç olduğunda bakılabilir.
Ayrıca LLM’ler var.
"Değişkenlik ve Reflection sorunu"
Bu, framework’ün nasıl çalıştığını anlamamaktan kaynaklanan bir endişedir.
Pratikte bunun yüzünden gerçek bir sorun çıkması neredeyse hiç olmaz.
Aksine, Reflection sayesinde nesne eşleme otomatikleştirilir ve üretkenlik büyük ölçüde artar.
"Lazy loading ve cache"
@Lazy’nin "en kötü teknoloji" olduğunu mu söylüyorsunuz? N+1 sorununu çözmek ve performansı optimize etmek için çok faydalı bir özelliktir.
Cache mekanizması da aksine performans iyileştirmesine büyük katkı sağlar.
"Bir tablodan yalnızca belirli sütunları almak"
JPQL veya Projection kullanılırsa yalnızca gerekli sütunlar kolayca sorgulanabilir.
Ve QueryDSL ile birlikte kullanılabilir.
ORM’nin amacının SQL’in yerini tamamen almak değil, geliştiricinin iş mantığına daha fazla odaklanmasına yardımcı olmak olduğunu düşünüyorum..
ORM konusunda karamsarım ama yeterince alternatif sunduğunu sanmıyorum.
İş ORM-yoğun ilerleyince gerçekten sonu gelmiyor; yukarıda da dendiği gibi, SQL dokümantasyonundan bile daha geniş bir doküman yığını içinde debelenirken tükenip gidebilirsiniz.
Son zamanlarda kişisel bir projede ORM kullanmadan geliştiriyorum ama yeniden kullanılabilirliği gözeten bir tasarım yapmaya çalışınca, bir noktada sanki kendi ORM’imi yapıyormuşum gibi bir yöne kayabildiğimi fark ediyorum. haha
Bir framework kullandığınız için, aynı framework’ü kullanan diğer geliştiricilerle ortak bir paradigma paylaşabilmeniz gibi bir noktanın, böyle şeyleri kullanmayın diyen yazılarda her zaman göz ardı edildiğini düşünüyorum.
Tablo sayısı çoksa ve kolonlar da fazlaysa (örneğin 50 tablo ve tablo başına 100’den fazla kolon) SQL’i doğrudan kullanınca resmen cehennem başlıyor.
Ama küçük bir servis yaparken JPA/Hibernate kullanmak bana göre çok büyük bir israf.
Sonuçta bu tür görüşler de yine tamamen duruma göre değişiyor gibi.
(Burada verilen örnek de 3-4 kolonlu bir şey zaten...)
Yukarıdaki yazıda son sorunun biraz düzeltilmesi gerekiyor gibi görünüyor.
Java ekosisteminde bunu 1. ORM ve 2. ORM dışı olarak özetleyebiliriz.
Hem 1’in hem 2’nin belirgin artıları ve eksileri olduğu için, yukarıdaki yazıdaki gibi uç bir sonuca varmak uygun değil.
Bizim durumda ise,
ORM olan JPA/Hibernate/QueryDSL kullanırken aynı zamanda MyBatis de kullanıyoruz.
ORM ile mümkün olduğunca verimliliği artırırken,
ORM’in kapsamakta zorlandığı sorgular için MyBatis kullanıyoruz.
Ayrıca yukarıda 1 veya 2’den hangisini seçerseniz seçin, SQL’i iyi bilmeniz gerekir.
Ben de düzeltmek isterdim ama sitede öyle bir özellik yok...
Sanırım ORM’nin başta neden popüler hale geldiğini görmezden geliyor gibiler.
Öğrenme maliyeti biraz olsa da, alışınca üretkenlik artışının çok net olduğu bir gerçek.
SQL basitmiş gibi görünebilir ama SQL’i satır satır elle yazmanın yorgunluğu var... Üstelik tablo değişince ilgili sorguların da hepsini tek tek değiştirmek gerekiyor; bu yüzden SQL bakımı da asla kolay bir iş değil. Küçük ve basit göründüğü kadar iş yükü artıyor (zaten bu yüzden üretkenlik konusu sürekli gündeme geliyor).
Ayrıca SQL’de ortaya çıkan hatalar runtime’da patladığı için yakalaması da zor oluyor. SQL injection gibi saldırılara karşı savunmayı da tek tek elle yaparken, sonuçta sorgu üreten kodlar eklenmeye başlanıyor (genelde basit şablonlarla başlayıp...). Böyle devam edince sonunda yine ORM benzeri bir şey ortaya çıkacak; o zaman doğrudan ORM kullanmak daha mantıklı değil mi?
Birkaç gün önce paylaşılmış olan yazı aklıma geldi.
https://tr.news.hada.io/topic?id=17955
Katılıyorum.
İnsanların ORM kullanma nedenini ve avantajlarını çoğu zaman yeterince iyi anlamadığını düşünüyorum.
Ayrıca, ORM üzerinden gerçekte çalıştırılan SQL’i analiz etmeye ya da anlamaya çalışan kişi sayısı da çok fazla değil gibi görünüyor.
Bunun, yalnızca bir kullanım kolaylığının ötesinde, SQL optimizasyonunu ve veritabanının nasıl çalıştığını derinlemesine anlamaya da yardımcı olabileceğinin daha fazla bilinmesi iyi olur.
Bence "kullanılmalı" ya da "kullanılmamalı" gibi iki uç noktaya kaymaya gerek yok haha;;
Ben, üretkenlik gerekiyorsa ORM'den yararlanıyorum,
ancak ORM ile karşılanamayan karmaşık sorguları veya daha fazla optimizasyon gerektiren sorguları raw query ile ele alıyorum.
Bence meselenin ORM mi yoksa raw query mi olduğu değil; hangi durumda neyi nasıl geliştireceğine göre uygun olanı seçmek.
Genel olarak, veritabanı normalizasyonu iyi yapılmışsa ve büyük ölçüde join gerektirmeyen veriler için bunun mümkün olduğunu düşünüyorum.
Ancak veritabanı normalizasyonundan başlayarak her şeyi bir DBA üzerinden düzgün şekilde yönetemiyorsanız, ORM de iyi bir seçenek olabilir diye düşünüyorum. Özellikle join ile alınan tarafta relationship olarak getirmenin sağladığı avantajlar, insanı neden ORM kullanmaya yönelttiğini gösteren çok iyi bir örnek bence.
Elbette framework'lerin geliştiricinin gelişimini sınırlayabildiği ve bu yüzden framework bağımlılığını azaltmamız gerektiği görüşüne katılıyorum ama
kesin bir şekilde ORM hiç kullanmayalım denmesi fikrine kolay kolay katılamıyorum.
Sanki bütün şirketlerde DBA varmış ve hepsi DDD ya da TDD gibi düzgün metodolojilerle geliştirme yapıyormuş varsayımı yapılıyor gibi geliyor.
Pratikte iş ortamında gerçekten öyle yapılacak olsa, kodun ne kadar daha büyük bir karmaşaya dönüşeceğini kestiremiyorum.
Backend’i PYTHON ile geliştirirken neredeyse her seferinde SQLALCHEMY ya da DJANGO ORM kullanıyorum.
Doğrudan SQL yazmakla ORM kullanmak arasında, ikisine de alışınca çok fark yokmuş gibi geldiği için bunu pek düşünmemiştim. Ama ORM kullanmamak gerektiğini savunan görüşler de varmış.
Framework bağımlılığını azaltma fikrine katılıyorum. Sadece DJANGO ORM kullanmayı bilip SQL ile çalışmayı hiç bilmiyor olmamak gerekir...
Hmm, pek öyle düşünmüyorum; ben karşı taraftayım. Şu anda yaklaşık 3 bin tablosu olan bir servisi işletiyorum ve domain o kadar karmaşık ki tek bir sorgu oluşturmak için bile temel olarak onlarca satır yazmam gerekiyordu. Buna dinamik sorguları da ekleyince iş gerçekten baş ağrıtıcı hale geliyor. Karmaşıklık yüzünden çok hata çıkıyor ve bakım da zorlaşıyordu. Ben karmaşık domainlerde ORM'nin daha avantajlı olduğunu düşünüyorum.
Benim durumumda normalleştirilmemiş bir db’yi bakımını yapma deneyimim oldu.
O zaman dinamik sorguları ORM kullanmadan, normal SQL ile yazıyordum,
Böyle olunca kodun daha da anlaşılması zor hale geldiği durumlar oluyordu.
Yalnızca karmaşık domainlerde değil, normalleştirmenin yetersiz olduğu domainlerde de bunu uygulamaya almak için yeterince alan olduğunu düşünüyorum.
Oh, o kadar da kötü bakmaya gerek yok sanırım.
Ben de kişisel olarak sadece SQL kullanmanızı tavsiye etmek isterim. JS dünyasında Prisma gibi araçlar çok kullanılıyor, ancak SQL o kadar da zor bir dil değil ve veritabanı I/O'su için fazla gereksiz soyutlama gerektiriyor gibi geldiğinden buna biraz mesafeli yaklaşıyorum.
Sanırım bunun nedeni, js/ts tarafındaki ORM'lerin özellikle çok aksak ürünler olması.
Jdbc gibi bir şey yeterli olur herhalde, değil mi? Daha önce birlikte çalıştığım birinin "JPA yavaş, başka bir şey kullan" dediğini hatırladım.
Efsane gibi bir hikâye.
Framework'ler yerine temellere geri dönmek mi trend oluyor diye düşündürüyor.
HTMX, SQL falan...
Ama tekerleği yeniden icat etmek zorunda kalma dezavantajı var.
Avantajları da var tabii..
2. MyBatis (ORM değil ama haha)
ORM yerine DAO'ya geçseymişim keşke.