Java ile Yansıma Tabanlı ORM Geliştirme
Yansıma tabanlı hafif bir ORM'de kritik maliyet SQL üretmekten çok üstveri eşleme, tür dönüşümü, transaction sınırı ve yansıma bilgisinin tekrar kullanılmasından doğar.
Değiştirilemeyen bir Oracle şeması üzerinde küçük bir yansıma tabanlı ORM geliştirirken ilk sorun SQL metni üretmek olmadı. Asıl maliyet, Java alanı ile fiziksel sütun arasındaki eşlemeyi her çağrıda yeniden keşfetmeden; NULL, anahtar, transaction ve şema yönlendirmesi gibi kuralları tek ve denetlenebilir bir sözleşmede tutmaktı. Üstelik canlı şemada kısıt veya tip tanımının uygulamanın beklediği kadar güçlü olmadığı durumlarda eşleme katmanının varsayımları sessiz veri bozulmasına dönüşebilir; bu yüzden üstverinin yalnız hızlı değil, başlangıçta doğrulanabilir olması gerekir.
Yansıma tekrar eden JDBC kodunu azaltıyor, fakat sıcak yolda bırakılırsa üstveri taraması, erişim kontrolü, geç hata ve gereksiz bellek ayırma üretir. Bu nedenle varlık üstverisini başlangıçta hesaplayıp önbelleğe almak, sütun sırasını ve tip dönüştürücülerini bir kez bağlamak, DML üretimini bu üstveri üzerinden yapmak ve bağlantı/transaction sınırını eşleme katmanından ayrı tutmak bu tasarımın temel performans kararıdır. Hata mümkünse ilk sorguda değil uygulama başlangıcında görünmelidir.
İlk bakışta yansıma, tekrar eden JDBC kodunu azaltan uygun bir araçtır. Alanlar ve anotasyonlar taranır, sütun listeleri çıkarılır ve INSERT, UPDATE veya DELETE ifadeleri otomatik üretilir. Yansıma her çağrıda çalıştırıldığında ise maliyet yalnız işlem süresi değildir. Hatalar çalışma anına taşınır, SQL üretimi veri akışının içine karışır ve aynı varlık için tekrar tekrar üstveri oluşturulur. Kritik sistemlerde kabul edilebilir tasarım, yansıma kullanımını sıcak işlem yolundan çıkarmalıdır.
Değiştirilemeyen eski veritabanı şemalarında yansımayı sıcak yoldan çıkarmak, eşleme üstverisini önceden hesaplamak, native DML ile transaction sınırlarını aynı sözleşmede tutmak ve şema yönlendirmesini ORM davranışından ayırmak doğrudan işletim gereksinimidir.
Eşleme modelinin kurulması
Bir ORM kütüphanesinin ilk katmanı varlık tanımıdır. Varlık sınıfı yalnızca bir Java veri taşıyıcısı olarak görülmemelidir. Aynı zamanda veritabanı nesnesine ilişkin sözleşmeyi de taşır. Tablo adı, alan ile sütun eşleşmesi, anahtar bilgisi, yazılabilir alanlar ve null davranışı açık biçimde tanımlanmalıdır.
Örnek bir varlık şu yapıda olabilir:
@DataTable(name = "APP_RECORD")
public final class ApplicationRecord {
@DataId
@DataColumn(name = "ID")
private Long id;
@DataColumn(name = "STATUS")
private Integer status;
@DataColumn(name = "CONTENT")
private String content;
@DataColumn(name = "UPDATED_AT")
private LocalDateTime updatedAt;
}Anotasyon sayısını artırmak her zaman daha doğru bir model üretmez. Veritabanı veya Java tür sistemi üzerinden güvenilir biçimde çıkarılabilen bilgiler anotasyonlarda tekrar edilirse iki ayrı doğruluk kaynağı oluşur. Alan adı ile sütun adı farklıysa açık bir eşleme gerekir. Bir alan yalnızca okunacaksa bu durum belirtilmelidir. Buna karşılık Java türünden, alan niteleyicilerinden veya sınıf yapısından güvenilir biçimde çıkarılabilen bilgiler için ek anotasyon tanımlamak gereksiz karmaşıklık üretir.
Eşleme katmanı en az şu sorulara kesin yanıt vermelidir:
- Varlık hangi tabloya karşılık gelir?
- Bir Java alanı hangi sütuna yazılır?
- Hangi alan birincil anahtardır?
- Anahtar uygulama tarafından mı, sequence ile mi üretilir?
- Null değer SQL "NULL" olarak mı yazılır, yoksa alan güncellemeden çıkarılır mı?
- Bir alan "INSERT", "UPDATE" veya iki işlem için de kullanılabilir mi?
- Oracle türü ile Java türü arasında hangi dönüşüm uygulanır?
Null davranışı özellikle önemlidir. "null" değerini sütunu güncellememek olarak yorumlamak ile sütuna "NULL" yazmak aynı işlem değildir. Bu iki anlam tek bir genel "update(varlık)" metodunda gizlenirse veri kaybı riski doğar. Alan seçimli güncelleme, açık bir update modeli veya ayrı bir komut nesnesiyle ifade edilmelidir.
Varlık nesnesinin mevcut değerleri üzerinden dinamik SQL üretmek mümkündür. Ancak her farklı null kombinasyonu için farklı SQL oluşturulması deyim önbellek verimini azaltabilir ve çalışma zamanındaki davranışı zorlaştırabilir. Kritik yazma akışlarında güncellenecek sütun kümesinin önceden tanımlanması, dinamik sütun seçiminin ise ayrı ve açık bir API üzerinden sunulması daha güvenlidir.
Yansıma maliyetini başlangıca taşımak
Yansıma tabanlı SQL üretimini her veri işleminde çalıştırmak yerine uygulama başlangıcına taşımak daha güvenli bir yapı oluşturur. Bir üstveri builder, varlık sınıflarını bir kez inceler. Elde edilen bilgiler alan, ekleme, güncelleme, silme ve varlık üstveri nesnelerine dönüştürülür. Ardından bu nesneler değişmez bir üstveri registry içinde tutulur.
Bu ayrım iki farklı çalışma evresi oluşturur:
- Başlangıç evresinde sınıflar taranır, eşlemeler doğrulanır ve SQL şablonları üretilir.
- Çalışma evresinde hazır üstveri okunur ve yalnızca parametre bağlama işlemi yapılır.
Örneğin "EntityMeta", ilgili varlık için gereken bütün bilgileri taşıyabilir:
public final class EntityMeta {
private final Class<?> entityType;
private final String tableName;
private final List<DataField> insertFields;
private final List<DataField> updateFields;
private final DataField idField;
private final InsertMeta insertMeta;
private final UpdateMeta updateMeta;
private final DeleteMeta deleteMeta;
}Gerçek uygulamada constructor doğrulamaları, null kontrolleri, savunmacı kopyalama ve değişmez koleksiyon oluşturma ayrıntıları bulunmalıdır. Buradaki temel nokta, üstveri nesnesinin üretildikten sonra değişmemesidir. Değişmez registry, eş zamanlı erişimde kilit gereksinimini azaltır. Aynı varlık için farklı iş parçacıklarıin farklı SQL üretme ihtimali ortadan kalkar.
Başlangıç doğrulaması yalnızca anotasyon varlığını kontrol etmemelidir. Aşağıdaki hatalar uygulama istek kabul etmeden önce bulunmalıdır:
- Aynı sütuna bağlanan birden fazla alan
- Birden fazla kimlik alanı
- Kimlik alanı bulunmayan update veya delete tanımı
- Yazılabilir alanı olmayan insert modeli
- Desteklenmeyen Java türü
- Geçersiz tablo veya sütun adı
- Parametre sayısı ile alan sayısı uyuşmazlığı
- Boş "UPDATE SET" bölümü
- Anahtar alanını yanlışlıkla güncelleyen SQL
- Aynı varlık için çelişen tablo tanımları
- Kalıtım yoluyla gelen yinelenen alan eşlemeleri
- Desteklenmeyen composite key tanımları
- Belirsiz null veya JDBC türü eşlemeleri
Bu doğrulamalar başarısız olduğunda uygulama başlamamalıdır. Hatalı üstveri ile kısmen çalışmak, kritik sistemlerde erken ve açık bir başlatma hatasından daha risklidir.
Alan sırası da açık biçimde belirlenmelidir. Yansıma API tarafından döndürülen alan sırasına doğrudan güvenmek yerine alanlar belirli bir kurala göre sıralanmalı veya üstveri üretimi sırasında sıraları sabitlenmelidir. SQL içindeki parametre sırası ile bağlayıcı tarafından kullanılan alan sırası aynı değişmez veri yapısından üretilmelidir.
Yansıma tamamen ortadan kalkmak zorunda değildir. Alan değerlerini okumak için "Field.get()" kullanılabilir. Daha ileri bir uygulamada "MethodHandle", "VarHandle" veya önceden hazırlanmış erişim planları tercih edilebilir. Ancak çoğu veritabanı işleminde bağlantı bekleme, ağ iletişimi ve SQL yürütme maliyeti alan erişim maliyetinden daha yüksektir. Optimizasyon kararı ölçümle verilmelidir. Temel kazanım, yansıma maliyetini azaltmaktan çok çalışma zamanı belirsizliğini başlangıç aşamasına taşımaktır.
Servis ve taşıma katmanı sınırı
ORM katmanının doğrudan denetleyici içinden kullanılması kolay görünür, ancak transaction ve iş kuralı sınırlarını HTTP katmanına taşır. Denetleyici, gelen isteği doğrulamalı ve uygun servis metoduna aktarmalıdır. Veri yazma kararı, transaction başlangıcı ve birden fazla DML işleminin sırası servis katmanında kalmalıdır.
Uygulama servisleri depo nesnelerini, transaction kapsamındaki bağlantı veya persistence context örneğini ve üstveri registry nesnesini kullanabilir. Kayıt başlatma, kayıt sonlandırma, metin güncelleme, medya durumu değiştirme veya kullanıcı etkileşimi kaydetme gibi akışlar gerçek iş işlemlerini temsil eder. Bunları tek bir genel "save(Object varlık)" metoduna indirgemek doğru değildir. Her işlemin hangi tabloları hangi sırayla değiştirdiği ve hata durumunda hangi adımların geri alınacağı farklı olabilir.
Public servis metodu transaction sınırını oluştururken insert, update ve delete gibi alt işlemler private tutulabilir:
@Transactional
public void saveContent(final ContentCommand command) {
updateContent(command);
updateStatus(command);
}
private void updateContent(final ContentCommand command) {
}
private void updateStatus(final ContentCommand command) {
}Bu yapı, bir iş işleminin tamamını tek transaction altında tutar. Denetleyicinin iki ayrı servis metodunu sırayla çağırması aynı atomikliği sağlamaz. İlk işlem commit edildikten sonra ikinci işlem hata verirse sistem kısmi durumda kalır.
Aynı sınıf içindeki private metotların ayrıca "@Transactional" olarak işaretlenmesi çoğu proxy tabanlı transaction altyapısında yeni bir transaction sınırı oluşturmaz. Transaction kapsamı, dışarıdan çağrılan public servis metodunda kurulmalıdır. İç çağrılar aynı transaction bağlamını paylaşmalıdır.
Denetleyici tarafında varlık nesnesini doğrudan HTTP girdisi olarak kabul etmek de sakıncalıdır. Veritabanı modelindeki her yazılabilir alanın dış istemciye açılması, istemcinin değiştirmemesi gereken sütunları etkileyebilmesine yol açar. İstek modeli ile varlık ayrı tutulmalıdır. Denetleyici komut nesnesini alır, servis iş kurallarını uygular ve ORM yalnızca gerekli alanları yazar.
ORM'nin genel olması, servislerin genel CRUD servisine dönüşmesini gerektirmez. Kritik iş akışlarında açık isimli metotlar daha güvenlidir. "saveMediaStatus" benzeri bir çağrının niyeti, "save(Object)" çağrısından daha kolay izlenir. SQL otomatik üretilebilir, ancak iş niyeti otomatikleştirilmemelidir.
Native DML ve sorgu yürütme
Değiştirilemeyen legacy şemalarda JPA varlık yaşam döngüsü her işlem için uygun değildir. Kısmi güncellemeler, şemaya özgü SQL yapıları, mevcut tabloların alışılmadık anahtar düzenleri ve trigger davranışları nedeniyle native DML daha doğrudan bir denetim sağlayabilir.
Hazırlanan üstveri üzerinden insert işlemi şu sırayı izler:
- Varlık türüne göre "EntityMeta" bulunur.
- Önceden üretilmiş SQL alınır.
- "PreparedStatement" oluşturulur.
- Alan değerleri belirlenen sırada bağlanır.
- Etkilenen satır sayısı doğrulanır.
- Anahtar üretilmişse sonuç varlık veya komut sonucuna aktarılır.
SQL metni her çağrıda birleştirilmemelidir. "InsertMeta" içinde sabit olarak tutulabilir:
INSERT INTO APP_RECORD
(ID, STATUS, CONTENT, UPDATED_AT)
VALUES
(?, ?, ?, ?)Parametre bağlama yalnızca "setObject()" çağrısına bırakılmamalıdır. Oracle JDBC sürücüsünün tür çıkarımı birçok yaygın türde çalışsa da tarih ve saat türleri, LOB alanları, null değerler ve sürüme bağlı sürücü davranışlarında açık JDBC türleri daha kararlı sonuç verir.
"LocalDate", "LocalDateTime", "OffsetDateTime", "byte[]", "CLOB", "BLOB", "BigDecimal" ve sayısal türler için merkezi bir parametre bağlayıcı kullanılmalıdır. Bu bağlayıcı Java türü, JDBC türü ve gerekiyorsa veritabanına özgü dönüşüm bilgisini üstveri üzerinden almalıdır.
Null değer bağlanırken JDBC türü bilinmelidir:
statement.setNull(index, Types.VARCHAR);Tür bilgisi "DataField" içinde önceden tutulabilir. Böylece çalışma zamanında alan türü yeniden incelenmez ve null değerler için sürücünün tahmin davranışına güvenilmez.
Update ve delete işlemlerinde etkilenen satır sayısı sessizce göz ardı edilmemelidir. Tek satır değiştirmesi gereken bir işlem sıfır veya birden fazla satır etkilediyse veri modeli, filtre veya transaction bağlamı beklenenden farklı olabilir. Bu durum kontrollü bir istisna üretmelidir.
Her sıfır satırlı update işlemi hata değildir. İdempotent komutlarda, koşullu güncellemelerde veya beklenen durum geçişlerinde sıfır satır geçerli olabilir. Beklenen satır sayısı operasyon üstverisinin veya servis metodunun sözleşmesinin parçası olmalıdır.
Legacy tabloda sürüm sütunu yoksa gerçek optimistic locking sağlandığı iddia edilmemelidir. Güncelleme koşuluna eski alan değerleri veya durum bilgisi eklenerek sınırlı bir çakışma denetimi yapılabilir:
UPDATE APP_RECORD
SET STATUS = ?
WHERE ID = ?
AND STATUS = ?Bu yaklaşım sürüm sütununun sağladığı genel optimistic locking semantiğiyle aynı değildir. Davranış açık biçimde tasarlanmalı, dokümante edilmeli ve eş zamanlı güncelleme testleriyle doğrulanmalıdır.
Şema ve bağlantı yönlendirmesi
Çok sayıda şema üzerinde çalışan sistemlerde veri erişim katmanının en riskli bölümlerinden biri bağlantı yönlendirmesidir. Yanlış şemaya gönderilen geçerli bir SQL ifadesi teknik olarak başarıyla çalışabilir ve hatanın fark edilmesini zorlaştırabilir.
Şema anahtarı request, iş parçacığı veya transaction bağlamında taşınıyorsa yaşam döngüsü açık biçimde yönetilmelidir. "ThreadLocal" tabanlı yönlendirme kullanıldığında bağlamın "finally" bloğunda temizlenmemesi, iş parçacığı havuzundaki sonraki isteğin önceki şemayı kullanmasına neden olabilir.
schemaContext.set(schemaKey);
try {
service.execute(command);
} finally {
schemaContext.clear();
}Sanal iş parçacıkları veya reaktif yürütme modellerinde klasik "ThreadLocal" varsayımları ayrıca değerlendirilmelidir. Bağlamın iş parçacığı kimliğine bağlı taşınması yerine açık parametre, scoped context veya kullanılan yürütme modeline uygun context propagation mekanizması tercih edilebilir.
Bilinmeyen şema anahtarında varsayılan şemaya sessiz geçiş yapılmamalıdır. Fail-fast davranışı, yanlış hedefe başarılı yazma yapılmasından daha güvenlidir. Yönlendirme kararı transaction başlamadan önce verilmelidir. Aynı transaction içinde bağlantı veya şema değiştirilmesi desteklenmiyorsa bu kısıt açık biçimde uygulanmalıdır.
Büyük sonuç kümelerinin işlenmesi
ORM katmanı yalnızca DML üretiminden sorumlu değildir. Büyük sonuç kümelerinin nasıl tüketileceği de veri erişim modelinin parçasıdır. Bütün satırları bir listeye yüklemek küçük veri kümelerinde kolaydır, ancak sonuç boyutu kontrol edilmediğinde öbek taşmasına yol açabilir.
Büyük sorgular için aşağıdaki yöntemlerden biri seçilmelidir:
- Sayfalama
- Imleç tabanlı artımlı okuma
- Geri çağırım veya satır tüketici kullanımı
- Kontrollü akış benzeri tüketim
- Veritabanı tarafında filtreleme ve projeksiyon
Sonuç akışı connection, deyim ve result set yaşam döngüsüne bağlıdır. Akışı servis metodunun dışına taşımak transaction veya bağlantı kapandıktan sonra veri okunmasına neden olabilir. Kaynak yaşam döngüsü tüketim modeliyle birlikte tasarlanmalıdır.
Getirme boyutu değeri sürücüye ve sorgu tipine bağlıdır. Büyük değerler gidiş-dönüş sayısını azaltırken bellek kullanımını artırabilir. Küçük değerler ise daha fazla ağ etkileşimi oluşturabilir. Bu değer sabit bir genel varsayım yerine gerçek veri boyutu ve gecikme ölçümleriyle belirlenmelidir.
Deneysel niteliğin sınırı
Kendi ORM kütüphanesini geliştirmek öğretici ve bazı özel sistemlerde işlevsel olabilir. Yine de ilk çalışan sürümün üretime hazır olduğu kabul edilemez. Deneysellik, kodun kurum içinde veya bireysel olarak geliştirilmesinden değil, davranış uzayının henüz yeterince sınanmamış olmasından kaynaklanır.
Bir ORM katmanı aşağıdaki alanlarda hata üretebilir:
- Tür dönüşümleri
- Null bağlama
- Transaction geri alma
- Bağlantı sızıntısı
- Büyük sonuç kümelerinde bellek taşması
- Aynı üstveri nesnesine eş zamanlı erişim
- Oracle hata kodlarının yanlış sınıflandırılması
- Şema yönlendirme bağlamının iş parçacığı üzerinde kalması
- Batch işlemlerinde kısmi başarı
- LOB akışlarının erken kapanması
- Saat dilimi dönüşümleri
- Yinelenen anahtar ve kısıt hataları
- Deyim zaman aşımı sonrasında bağlantının belirsiz durumda kalması
- Commit sonucunun istemci tarafından doğrulanamaması
- Yeniden deneme sırasında aynı yazma işleminin yinelenmesi
Birim testleri yalnızca SQL metnini doğrular. Gerçek Oracle sürücüsü ve gerçek tablo tipleriyle entegrasyon testleri gerekir. Transaction rollback, bağlantı kopması, deyim zaman aşımı, kilitlenme, kısıt ihlali ve havuz tükenmesi kontrollü biçimde üretilmelidir.
Yansıma tabanlı üstveri için özellik based test yaklaşımı da yararlıdır. Farklı alan kombinasyonları, kalıtım yapıları, erişim niteleyicileri ve anotasyon hataları otomatik üretilerek builder'ın geçersiz modelleri reddettiği doğrulanabilir.
Performans testi tek iş parçacığı ile yapılan mikro benchmarktan ibaret olmamalıdır. En az şu değerler ölçülmelidir:
- Üstveri erişim gecikmesi
- Parametre bağlama maliyeti
- Saniyedeki işlem sayısı
- P95 ve P99 transaction süresi
- Bağlantı bekleme süresi
- Büyük sonuç kümelerinde öbek kullanımı
- Eş zamanlı işlem altında hata oranı
- Uzun süreli testte bağlantı ve nesne sızıntısı
- Deyim önbellek isabet oranı
- Garbage collection duraklamaları
- Kuyruk uzunluğu ve doygunluk davranışı
ORM katmanının doğrudan JDBC çağrısına göre birkaç mikrosaniyelik ek maliyeti çoğu sistemde kabul edilebilir olabilir. Ancak çalışma zamanında sınıf taraması, SQL birleştirme ve yinelenen koleksiyon üretimi yapılıyorsa bu maliyet yüksek trafik altında birikir. Ölçüm yalnızca ortalama süreyi değil, kuyruk altında oluşan uzun gecikmeleri ve kaynak doygunluğunu da göstermelidir.
Hata sınıflandırması ve yeniden deneme politikası
Veritabanı hatalarının tamamı aynı genel istisna içine kapatılmamalıdır. Üst katman, hatanın tekrar denenebilir olup olmadığını ve transaction sonucunun kesin olarak bilinip bilinmediğini ayırt edebilmelidir.
Hata modeli en az şu sınıfları ayırmalıdır:
- Kısıt ihlali
- Yinelenen anahtar
- Veri dönüşüm hatası
- Bağlantı kurma hatası
- Bağlantı kaybı
- Deyim zaman aşımı
- Kilitlenme
- Lock zaman aşımı
- Beklenmeyen satır sayısı
- Şema yönlendirme hatası
- Desteklenmeyen üstveri
- Belirsiz commit sonucu
Yazma işlemlerinde kör yeniden deneme uygulanmamalıdır. Bağlantı commit sırasında kopmuşsa istemci işlemin commit edilip edilmediğini kesin olarak bilemeyebilir. Aynı işlemi tekrar göndermek yinelenen veri üretebilir. Yeniden deneme ancak işlem idempotent ise veya iş anahtarı, unique kısıt ve yinelenen kayıtları tekilleştirme mekanizmasıyla yinelenme güvenli biçimde engelleniyorsa uygulanmalıdır.
Kilitlenme gibi açıkça rollback ile sonuçlanan durumlarda sınırlı yeniden deneme mümkün olabilir. Buna karşılık veri doğrulama, kısıt ihlali veya yanlış şema gibi kalıcı hatalar tekrar denenmemelidir. Yeniden deneme politikası istisna türüne, operasyonun idempotency özelliğine ve transaction sonucunun kesinliğine göre belirlenmelidir.
Kritik sistemde canlıya alma ölçütleri
Yansıma tabanlı özel bir ORM kütüphanesi kritik sistemlerde kullanılabilir. Bunun için kütüphanenin deneysel niteliği kontrollü bir ürünleştirme süreciyle sınırlandırılmalıdır.
İlk koşul kapsamın dar tutulmasıdır. Kütüphane bütün JPA özelliklerini yeniden üretmeye çalışmamalıdır. Gerekli kapsam bağlantı ve şema yönlendirme, sorgu yürütme, native DML, transaction uyumu ve kontrollü sonuç işleme ise geliştirme bu sınırda kalmalıdır. Lazy loading, varlık graph, otomatik ilişki yönetimi ve genel sorgu dili gibi özellikler gerçek ihtiyaç bulunmadıkça eklenmemelidir.
İkinci koşul deterministik davranıştır. Aynı varlık üstverisi her başlangıçta aynı SQL'i üretmelidir. Alan sırası yansıma API'nin tesadüfi dönüş sırasına bırakılmamalıdır. Registry başlatıldıktan sonra değişmemelidir. Geçersiz model ilk istek sırasında değil, başlangıçta reddedilmelidir.
Üçüncü koşul hata modelinin açık olmasıdır. Kısıt ihlali, bağlantı hatası, zaman aşımı, kilitlenme, veri bulunamaması ve beklenmeyen satır sayısı aynı genel istisna içine kapatılmamalıdır. Üst katman hangi hatanın tekrar denenebilir olduğunu bilmelidir. Yazma işlemlerinde kör yeniden deneme uygulanmamalıdır.
Dördüncü koşul gözlemlenebilirliktir. SQL metninin tamamını ve kişisel verileri loglamak yerine işlem adı, varlık türü, süre, etkilenen satır sayısı, şema anahtarı ve hata sınıfı izlenmelidir. Yavaş işlemler, bağlantı beklemeleri ve havuz doygunluğu ayrı metriklerle takip edilmelidir.
Parametre değerleri varsayılan olarak loglanmamalıdır. Hata ayıklama amacıyla SQL şablonu kaydedilecekse hassas alanlar maskelenmeli ve log erişimi sınırlandırılmalıdır. Trace veya correlation kimliği, aynı iş işlemi içindeki birden fazla DML adımının ilişkilendirilmesini sağlamalıdır.
Beşinci koşul geri dönüş planıdır. Yeni ORM katmanı önce salt okunur veya kritik olmayan akışlarda çalıştırılabilir. Eski veri erişim katmanıyla gölge karşılaştırma yapılabilir. Aynı sorguların sonuçları karşılaştırılır. Yazma işlemleri için kontrollü trafik, özellik bayrağı ve hızlı devre dışı bırakma mekanizması gerekir.
Canlıya alma kararı şu kanıtlar elde edilmeden verilmemelidir:
- Desteklenen bütün Java ve Oracle türleri test edilmiş olmalıdır.
- Transaction atomikliği hata enjeksiyonuyla doğrulanmalıdır.
- Bağlantı, deyim ve result set sızıntısı bulunmamalıdır.
- Uzun süreli yük testi kararlı sonuç vermelidir.
- Şema yönlendirme hataları sessiz fallback üretmemelidir.
- Üretilen SQL başlangıçta doğrulanmalıdır.
- Kritik DML işlemlerinde satır sayısı denetlenmelidir.
- Kütüphanenin desteklemediği davranışlar belgelenmelidir.
- Eski erişim yöntemine dönüş prosedürü sınanmalıdır.
- Kod en az bir bağımsız teknik incelemeden geçmelidir.
- Yeniden deneme ve idempotency davranışı hata senaryolarıyla doğrulanmalıdır.
- Hassas verilerin loglara taşınmadığı gösterilmelidir.
- Bağlantı havuzları doygunluğunda sistemin kontrollü biçimde hata verdiği kanıtlanmalıdır.
Bu koşullar sağlandığında yansıma kullanımı tek başına üretim riski oluşturmaz. Risk, yansıma ile ne zaman ve ne kadar iş yapıldığından doğar. Sınıf analizi başlangıçta tamamlanıyor, üstveri değişmez tutuluyor ve sıcak işlem yolunda yalnızca hazır erişim planları kullanılıyorsa davranış daha öngörülebilir hale gelir.
Özel amaçlı bir ORM katmanı deneysel bir fikir olarak başlayabilir, ancak deneysel kalmak zorunda değildir. Kütüphanenin canlı sistemdeki güvenilirliği özellik sayısıyla değil, sınırlarının açıklığıyla ölçülür. Denetleyici taşıma katmanında, servis iş ve transaction sınırında, ORM ise yalnızca veri erişim sorumluluğunda kaldığında mimari denetlenebilir olur.
Bu tür özel ORM katmanında “çalışıyor” ölçütü yeterli değildir. Aynı varlık için üstverinin tekrar üretilmemesi, transaction sınırının belirsizleşmemesi, şema yönlendirmesinin deterministik kalması ve hata koşullarının ölçülmüş olması gerekir. Yansıma ancak sıcak yoldan çıkarıldığında bakım kolaylığı ile çalışma zamanı maliyeti arasında anlamlı bir denge kurar.
Kaynakça
- Martin Fowler. (2002). Patterns of Enterprise Application Architecture. Addison-Wesley.
- Oracle. (2021). Java Platform, Standard Edition 17 API - java.lang.reflect. Oracle. URL
- Ron Pressler; Alan Bateman. (2023). JEP 444: Virtual Threads. OpenJDK. URL