Nesne – İlişkisel Eşleştirme ve JPA (Java Persistence API)

Giriş

Nesne-merkezli diller ile geliştirme yapanlar, tabii olarak düşünce sistemlerini nesne kavramı etrafında örerler. Nesne kültürü bize, yazılım projelerinin her safhasında, farklı soyutlama seviyelerinde ya da farklı açılardan yaklaşarak nesneler hakkında düşünme imkanı sunar. Geliştirilecek olan yazılımın ihtiyaçlarınından yola çıkarak nesnelerin sorumlulukları ve özellikleri, nesneler arasındaki ilişkiler, çalışma zamanında nesnelerin birbirleriyle nasıl haberleşecekleri vs. cinsinden düşünceler zihnimizde uçuşur dururlar. Hatta nesneler hakkında bu şekilde düşünebilmeyi ayrıcalık olarak bile görürüz.

Nesneler hakkındaki düşünce faaliyetimiz kodlamada hayata geçer ve nesneleri oluşturur, birbirleriyle ilişkilendirir ve yazılım sistemimizi çalışır hale getiririz. Buraya kadar herşey güzel de nesnelerimizi nerede saklayacağız sorusu gündeme gelince işin tatı biraz kaçar, şöyle ki: Yazılımın çalışması sırasında üretilen nesnelerin taşıdıkları bilgilerin daha sonra kullanılmak üzere kalıcı hale getirilmesinin en tabii yöntemi, olsa olsa bir nesne-merkezli veri tabanı yönetim sistemi (NMVTYS) (object-oriented database management system, OODBMS) kullanmaktır. Fakat gerçekte durum biraz farklıdır. Yazılımın ilk günlerinden bu yana yazılımcılar, nesnelerini kalıcı kılmak için farklı çözümler geliştirmişler ama burada bahsedilmesi gerekmeyen çok değişik nedenlerden dolayı bu çözümlerden sadece ilişkisel veri tabanı yönetim sistemleri (İVTYS) (Relational Database Management Systems, RDBMS) günümüzde en yaygın kullanılan olmayı başarmışlardır. İlişkisel veri tabanları, adından da anlaşılacağı üzere, uygulamalarda nesnelerde ifade ettiğimiz durum bilgilerini, bu bilgiler arasındaki ortak alanlardan yola çıkarak, tablolar ve tablolar arası ilişkilerle tanımlamayı hedef edinmiştir. Bu şekilde, bilginin en az yer kaplayarak, sağlıklı bir şekilde saklanması, korunması ve gerektiğinde tekrar sunulması amaçlanmıştır. Teorik altyapısı 70’li yıllarda atılan ve geçen 40 yılda olgunlaşarak günümüze gelen ve kurumsal uygulamaların gerektirdiği özellik ve hizmetleri bünyesinde barındıran İVTYS, hem Oracle, IBM, Microsoft gibi bu dünyanın öncü şirketleri tarafından değişik ticari markalar altında lisanslı olarak hem de örneğin Sun tarafından MySQL gibi açık kaynak kodlu ve ticari olmayan lisanslarla piyasaya sunulmaktadır. Veriyi güvenli ve sağlıklı olarak saklama ve sunma konusunda kurumsal uygulamalarda gerekli olan pek çok özelliği bünyesinde barındıran İVTYS, kullanım yaygınlığı açısından NMVTYSne tartışılmayacak bir üstünlük sağlamıştırlar. Bu üstünlüğü ve sebeplerini tartışmanın yeri burası olmamakla birlikte genel olarak NMVTYS kullanımı özellikle ülkemizde ancak ya bir akademik araştırmanın ya da tatminsiz programcıların geç saatlere varan meraklı çalışmalarının konusu olabilir. Dolayısıyla yazının bundan sonraki kısmında nesne-merkezli programların, sadece İVTYS (ya da bundan sonra sadece “veri tabanı”) kullanması ele alınacaktır.

Uygulamadaki sınıflar/nesneler ile aralarındaki ilişkilerin ifade ediliş şekli, veri tabanındaki tablolar ve aralarındaki ilişkilerin ifade edilişinden pek çok bakımdan farklılık arzeder. Bu iki sistem, sarmalama (encapsulation), tip, erişim, güvenlik vb. pek çok temel konuda, değişik çıkış noktalarına, dolayısıyla da ilgi alanlarına sahip olmalarından dolayı, farklı yaklaşımlar benimsemişlerdir. Örneğin, nesne dünyasında, iki nesne arasındaki ilişki referanslar üzerinde yürürken veri tabanında böyle bir durum sözkonusu değildir. Ya da uygulama programcıları, uygulamadaki nesnelerin içerikleriyle ilgilenirlerken kimlik (identity) bilgisini veri tabanı programcıları kadar önemsemezler. Veri tabanındaki bilgilerin yani tablolardaki satırların bir şekilde birbirlerinden farklı olması gereksinimi, veri tabanlarının varlık sebeplerinin ve sağlıklı çalışma prensiplerinin başında gelmektedir. Nesneler ile ilişkisel tablolar arasında bu türden farklılıkların oluşturduğu duruma nesne-ilişkisel uyumsuzluğu (object-relational mismatch) denir. (Bütün bu tartışmada her nesne-merkezli programcının yaptığı gibi benim de “nesne” ile “sınıf” kavramını birbirlerinin yerine geçer şekilde kullandığımı belirtmeliyim.)

Nesnelerin tablolarda kalıcı hale getirilmeleri için önce, uygulamadaki sınıfların veri tabanındaki tablolara, sınıflarda tanımlanan nesne özelliklerinin yani nesne değişkenlerinin (instance/object variables) de bu tablolardaki sütunlara/kolonlara karşılık getirilmesi ya da bir başka deyişle eşleştirilmesi gerekmektedir. Buna “nesne-ilişkisel eşleştirme” (NİE) (Object-Relational Mapping, ORM) denir. Böylece uygulamada, çalışma zamanında sınıflardan oluşturulan nesneler, tablolardaki satırlara yazılarak kalıcı hale getirilirler. Uygulama aynı zamanda, tablolardaki satırların sorgulanması (querying), güncellenmesi, silinmesi gibi farklı işlemeleri yapmaya ihtiyaç duyar. Programlama dillerinde bütün bu işlemleri yapan ODBC ya da JDBC (Java’nın veri tabanı erişim API’si) gibi veri tabanı erişim yapıları vardır. Bu yapılar veri tabanlarına, kendilerine has protokolları kullanan sürücüler (driver) üzerinden erişerek, programcılara, veri tabanında yukarıda bahsedilen türden işlem yapabilme imkanı verirler. Bu tür veri tabanı iletişim yapıları temel olarak, tablo ve tablolara yazılan satırlar ile ilgili yaratma, okuma, güncelleme ve silme ya da kısaca YOGS (create, read, update, delete, CRUD) işlemleri yapmaya yarayan API’leri, programcıların kullanımına sunarlar.

SQL (Structured Query Language), İVTYSlerinin ortak dilidir. Veri tabanı programcıları SQL’i, veri tabanındaki tablolar ile bu tabloların satırlarında bulunan ve gerçekte uygulamadaki nesnelerin değişkenlerini oluşturan verileri işlemede kullanırlar. Uygulama programcıları da programlama dillerindeki veri tabanı erişimi sağlayan yapılarda SQL sorgularını kullanırlar ve bu dil yardımıyla YOGS ile ifade edilen veri tabanı işlemlerini yaparlar. (Tablolar ve tablolardaki satırlar ile ilgili işlemler genel olarak SQL’in veri tanımlama (Data Definition Language, DDL) ve veri işleme (Data Manipulation Language) özelliklerini kullanarak yapılmaktadır.) Bu durum, veri tabanında kalıcı kılınacak her nesne için önce nesne-ilişkisel eşleştirmenin yapılması ve sonra da YOGS sorgularının yazılmasını gerektirir. Konu üzerinde biraz düşünüp pratik yapınca, yukarıda bahsedilen nesne-ilişkisel uyumsuzluğu pek çok açıdan sıkıntı verici hale gelir. Örneğin nesnelerin ve aralarındaki ilişkilerin karmaşıklığı, ilişkilerin bir kısmının, veri tabanında ifadesi söz konusu olmayan miras/devralma/kalıtım (inheritance) ve çok şekillilik (polymorphism) gibi yapılarla ifade edilmesi, ilk etapta akla gelen “her nesnenin bir tabloya karşılık gelmesi” gibi bir çözümün safça bir kabul olduğunu ortaya çıkarır. Dolayısıyla uygulama programcılarının SQL kullanarak karmaşık nesne yapılarını tablolar ve satırlarına karşılık getirmesi ve sonrasında da YOGS sorgularını yine SQL’i kullanarak yazmaları, ciddi bir iş yükü ortaya çıkarır. Veri katmanını (persistence layer) oluşturma olarak adlandırılabilecek olan böyle bir faaliyetin, veri tabanı bağlantı havuzu (connection pooling), işlem yönetimi (transaction management), ön bellek kullanımı (caching), veri tabanından gelecek sıradışı durumların yönetimi (exception management), sonradan yükleme (lazy loading) ve ard arda işleme (cascading), performans ve ölçeklenirlik (scalability) vb. pek çok konuyu da halletmesi gerekmektedir. Bu durum harcı alem olmayan hemen her projede veri katmanı çalışmalarının, projenin en önemli ve yoğun kısımlarından biri haline gelmesine sebep olmaktadır.

Nesne-merkezli uygulamalar için en tabii kalıcılık yönteminin nesne-merkezli veri tabanı kullanmak olduğunu daha önce belirtmiştik. Fakat böyle bir çözümün kurumsal projelerde uygulanabilirliği bu yazıda tartışılamayacak sebeplerden dolayı gerçekçi görünmemektedir. Bu durum taa Smalltalk, C++ ve Delphi günlerinden bu yana böyledir. Bu diller ile geliştirilen uygulamalarda nesne-ilişkisel uyumsuzluğunu tekrar tekrar yaşayan programcıların bu problemi aşmak için geliştirdikleri çözüm, İVTYS önüne konulacak bir katmanla, İVTYSnin nesne-merkezli bir veri tabanına dönüştürülmesidir. Yani İVTYS ile uygulama arasına konulacak böyle bir katmanın iki arayüzü olacaktır: Birisi SQL’ı kullanarak İVTYS ile haberleşen arayüz, diğeri ise bir API üzerinden nesnelerle alakalı YOGS işlemlerini yapmaya izin veren arayüz. Dolayısıyla bu katman, uygulama programcılarını, arka taraftaki İVTYSinin ilişikisel yapısından uzak tutacak ve onlara, nesneleri sanki NMVTYSinde kalıcı kılıyormuş gibi program yazma imkanı sağlayacaktır. Böyle bir çözüm, geliştirilmesi ve adaptasyonu zaman ve bilgi açısından zor olmakla birlikte, bir kurumun, kurumsal BT mimarisinin bir parçası haline getirildiğinde, uygulama geliştirmede çok ciddi zaman ve gayret kazanımı sağlayacağı da açıktır. Bu sebeple tarihi olarak nesne-merkezli dillerle geliştirme yapan kurumların bir kısmı ya kendi NİE araçlarını yazmış ya da o dillerde piyasada ticari olarak var olan NİE araçlarını elde etmeyi tercih etmiştir.


NİE Araçlarının Kısa Tarihi

NİE araçlarını genel olarak Java-öncesi ve Java-sonrası diye ikiye ayırdığımızda, ilk dönem ile ilgili akılda kalan isimlerin en önemlisi, Rogue Wave’in C++ için geliştirdiği ve eski ismi DBTools.h++ olan SourcePro DB’si ile The Object People’ın Smalltalk için geliştirdiği TopLink’tir. Java döneminde ise pek çok NİE aracı çıkmıştır. Java’ın ilk günlerinde pek çok yazılım evi JDBC’yi kullanarak kendi NİE aracını yazmayı denemişti. Java dünyasında sanırım ilk çıkan NİE aracı Sun’ın Java için yazıp ticari olarak piyasaya sürdüğü JavaBlend’tir. (Bu satırların yazarı, bu NİE aracının fiyatını, Sun ile görüşerek öğrenmişti. Nostaljik bir bilgi olarak hakkında 1997 yılında yayınlanmış bir haberi http://www.ncns.com/news/897/javablend.html adresinden bulabileceğiniz JavaBlend’in o zamanki birim lisans fiyatının $150.000 civarında olduğunu hatırlıyorum. Ama günümüzde cevval bir üniversite öğrencisi eğitiminin 2. senesinde Hibernate gibi bir NİE aracını kullanabiliyor. Bu da Java kültürünün bize kazandırdıklarından olsa gerek.) Daha sonraki yıllarda Sun JavaBlend’i geliştirmeye devam etmedi ve bu ürün piyasadan çekildi.

Kurumsal Java yani Java EE’nin o zamanki adıyla J2EE’nin ilk sürümü olan Aralık 1999 tarihli 1.1 versiyonunda kurumsal Java bileşenlerinin (Enterprise JavaBean ya da EJB) iki türünden birisi olan entity bean, bir NİE aracı idi ve Java ile geliştirilen uygulamaların veri katmanını halledecek şekilde geliştirilmişti. J2EE uyumlu uygulama sunucularının (application server) sağladıkları ayar (configuration) yapılarından da yararlanarak, uygulmadaki sınıflar, veri tabanındaki tablolarla eşleştiriliyor ve programcının isteğine göre YODS sorguları, uygulama sunucusu tarafından çalışma zamanında oluşturuluyordu. İçinde bulunulan sunucu tarafından yönetilen bu veri katmanı yapısının (Container-Managed Persistence, CMP) yetersiz kaldığı durumlarda programcı YODS sorgularını kendisi yazmak suretiyle (Bean-Managed Persistency, BMP) daha etkin yapılar da oluşturabilmekteydi.

Tabi EJB kavramı, Java’ya kadar farklı dillerde birbirinden farklı mimari kabuller ve yaklaşımlar içeren, çok farklı uygulama ve araç tarafından bölük börçük yerine getirilen orta katman (middleware) servislerini tek bir mimari yaklaşım içerisinde halletmenin ilk girişimi olduğu için, pek çok sıkıntıyı da beraberinde getirmişti. Büyük bir hüsnü kabul ile karşılanmasına rağmen EJB’ler ve özellikle de entity beanleri kısa sürede hayal kırıklığı yarattı. Aynı zamanda birer uzak/dağıtık (remote ya da distributed) nesne olan EJBler, gerek işin doğasının zaten çok zor olması gerek ise aşırı akademik yaklaşımdan dolayı kullanıcılarına öğrenme, uygulama ve özellikle de çalışma zamanındaki performans ve ölçeklenirlik açısından ciddi sıkıntılar yaşattı. Her ne kadar Sun iki sene sonra, 2001 güzünde, EJB 2.0’ı yayınlayıp, özellikle performans ve ölçeklenirlik konusunda belli ilerlemeler sağladıysa da entity beanlere olan güvenin azalmış olması ve en iyi uygulamalarda (best practices) “entity beanleri kullanmayın” (don’t use entity beans) gibi tavsiyelerin sıklıkla geçmeye başlaması gerek Sun’ı gerek ise Java programcılarını veri katmanınında kullanacakları NİE aracı konusunda alternatifler aramaya itti. Bu süreçte Castor (http://www.castor.org), iBatis (http://ibatis.apache.org/) ve Hibernate (http://www.hibernate.org) gibi bazıları açık kaynak kodlu bağımsız NİE araçları ortaya çıktı. Bu araçlar Javacılar tarafından büyük bir kabul ile uygulamaya kondu. Bu tür araçların entity beanlerden en büyük farkı, çalışmak için bir uygulama sunucusu gerektirmiyor olup, sadece NİE konusuna odaklanmış, daha hafif ve daha hızlı öğrenilip kullanılan bileşenler olmalarıydı.

Entity beanlerdeki sıkıntının farkında olan Sun 2000’li yılların başında konu ile alakalı JDO (Java Data Objects) isimli bir çözümü dünyaya duyurdu. Bir NİE çözümü olarak farklı bir yöntem izleyen JDO, nesne kalıcılığına temel olarak, daha fazla NMVTYS üreticilerinin etkisindeki ODMG’nin (http://www.odbms.org/odmg/) yaklaşımını benimsemişti. Temel farklılık, JDO ile kalıcı kılınacak nesnelerin bir zenginleştirme (enhancement) sürecinden geçirilmesi gerekmesiydi. Çalışma zamanında bir uygulama sunucusuna ihtiyaç da duymayan JDO’yu, Java’ya en başından bu yana sarılan Oracle ve IBM gibi şirketlerden hiçbirisi desteklemedi ve bu şirketler herhangi bir JDO ürünü geliştirip Javacıların kullanımına sunmadı. Dolayısıyla da JDO yaygın bir kullanım alanı bulamadı.

Yukarıda entity beanlerin problemlerinden dolayı veri katmanını EJB’ler üzerine bina etmekten vazgeçen Java toplumunun kendi içinden bağımsız NİE araçları çıkardığından bahsetmiştik. Bu araçlardan Hibernate, NİE problemini çözmek için basit ama etkin bir yaklaşım oluşturmuş ve kullanıcı topluluğundan gelen geri beslemeler ile çok daha başarıyla kullanılabilir hale gelmişti. Tabi olarak bu durum Hibernate’in kullanımını gittikçe yaygınlaştırdı.

Ayrıca Smalltalk dünyasında ciddi bir tecrübe kazanan TopLink’in 90’lı yıllarda zaten bir Java versiyonu çıkarılmıştı. Sonrasında, önce WebGain daha sonra da 2002’de Oracle tarafından satın alındı ve Oracle’ın Fusion Middleware’inin bir parçası olarak Javacıların kullanımına sunuldu.

JPA

Yukarıda bahsi geçen bütün bu bağımsız NİE araçları az ya da çok kendi çevrelerinde bir kullanıcı kitlesi çekmeyi başarmışlardı. Özellikle Hibernate bu konuda lider durumdaydı. Bu araçlarla alakalı en temel problem hepsinin farklı bir yaklaşıma dolayısıyla da bir API’ye sahip olmalarıydı. Bu yüzden bu çözümler birbirlerinin yerine geçebilir durumda değillerdi. Bu süreçte Sun, NİE problemine bir başka çözüm önermek üzere yola çıktı ve eski tecrübeleri ile Toplink ve Hibernate gibi başarılı araçların birikimlerini bir araya getirerek JPA (Java Persistence API) isimli yeni bileşeni yayınladı. Ana teması “daha kolay geliştirme” olan Java EE 5’in içindeki EJB 3.0’ın (http://jcp.org/en/jsr/detail?id=220) bir parçası olarak 2006 yılında Javacılara sunulan JPA 1.0, JavaBlend ile başlayıp, başarısız entity bean, başarılı ama yaygınlaşamayan JDO ve çoğu başarılı bağımsız NİE araçlarının en iyi yaklaşımlarını bir araya getirdiği için çok daha büyük bir ilgiyle karşılaştı. Bağımsız NİE üreticilerinin pek çoğu, örneğin Hibernate ve TopLink, JPA uyumlu ürünlerini peşi sıra piyasaya çıkardılar. (Dolayısıyla Hibernate ve TopLink gibi ürünlerle hem JPA hem de kendi özel (native) API’leri üzerinden programlama yapılabilmektedir.)Bu durum en başından bu yana tanım (specification) tabanlı ilerleyen Java’nın yapısına çok daha uygundu ve Javacılar artık standart bir API üzerinden pek çok farklı NEI ürününü kullanabilir hale gelmişlerdi.

Bu yazı toplam 2553 defa görüntülenmiştir.