2. Katmanlı bir Java uygulamasının mimarisi
Bir Java uygulaması genellikle her biri açıkça tanımlanmış bir role sahip katmanlara ayrılır. Yaygın olarak kullanılan üç katmanlı mimariyi ele alalım:
![]() |
- [1] katmanı, burada [ui] (Kullanıcı Arayüzü) olarak adlandırılır ve bir Swing grafik arayüzü, konsol arayüzü veya web arayüzü aracılığıyla kullanıcıyla etkileşime giren katmandır. Bu katmanın görevi, kullanıcıdan gelen verileri [2] katmanına iletmek ya da [2] katmanı tarafından sağlanan verileri kullanıcıya sunmaktır.
- [2] katmanı (burada [metier] olarak anılmaktadır), iş kuralları olarak adlandırılan c.a.d kurallarını uygulayan katmandır. Uygulamaya özgü mantık, kendisine verilen verilerin nereden geldiğini veya ürettiği sonuçların nereye gittiğini dikkate almadan çalışır.
- [3] katmanı, burada [DAO] (Veri Erişim Nesnesi) olarak anılan bu katman, [2] katmanına önceden kaydedilmiş verileri (dosyalar, veritabanları, ...) sağlayan ve [2] katmanı tarafından sağlanan sonuçların bir kısmını kaydeden katmandır.
[DAO] katmanını uygulamak için çeşitli seçenekler mevcuttur. Bunlardan birkaçını inceleyelim:
![]() |
Yukarıdaki [JDBC] katmanı, Java'da veritabanlarına erişmek için kullanılan standart katmandır. Bu katman, [DAO] katmanını, veritabanını yöneten SGBD katmanından izole eder. Teorik olarak, [DAO] katmanının kodunu değiştirmeden SGBD'i değiştirmek mümkündür. Bu avantaja rağmen, API ve JDBC katmanlarının bazı dezavantajları vardır:
- SGBD üzerindeki tüm işlemler, SQLException kontrollü istisnasını tetikleyebilir. Bu durum, çağıran kodu (burada [DAO] katmanı) bu işlemleri try / catch bloklarıyla çevrelemek zorunda bırakır ve böylece kodu oldukça ağırlaştırır.
- [DAO] katmanı, SGBD'e karşı tamamen bağımlı değildir. Örneğin, bu katmanlar birincil anahtar değerlerinin otomatik olarak oluşturulmasına ilişkin özel yöntemlere sahiptir ve [DAO] katmanı bunları göz ardı edemez. Dolayısıyla, bir kayıt eklenirken:
- Oracle ile, [DAO] katmanı önce kaydın birincil anahtarı için bir değer almalı, ardından kaydı eklemelidir.
- SQL Sunucusu ile ise, [DAO] katmanı kaydı ekler; bu kayda SGBD tarafından otomatik olarak bir birincil anahtar değeri atanır ve bu değer [DAO] katmanına iletilir.
Bu farklılıklar, depolanmış yordamların kullanılmasıyla ortadan kaldırılabilir. Önceki örnekte, [DAO] katmanı, SGBD'in özelliklerini dikkate alacak şekilde Oracle veya SQL Sunucusu'nda depolanmış bir yordamı çağırır. Bu özellikler, [DAO] katmanında gizlenecektir. Bununla birlikte, SGBD'in değiştirilmesi, [DAO] katmanının yeniden yazılmasını gerektirmeyecek olsa da, yine de saklı yordamların yeniden yazılmasını gerektirecektir. Bu, engelleyici bir faktör olarak değerlendirilmeyebilir.
[DAO] katmanını, SGBD'in özel özelliklerinden ayırmak için birçok çaba sarf edilmiştir. Son yıllarda bu alanda gerçek bir başarı elde eden bir çözüm, Hibernate'in sunduğu çözümdür:
![]() |
[Hibernate] katmanı, geliştirici tarafından yazılan [DAO] katmanı ile [JDBC] katmanı arasına yerleştirilir. Hibernate, bir ORM (Nesne-İlişkisel Eşleştirici) olup, ilişkisel veritabanları dünyası ile Java tarafından işlenen nesneler dünyası arasında köprü kuran bir araçtır. [DAO] katmanının geliştiricisi, artık [JDBC] katmanını veya içeriğini kullanmak istediği veritabanı tablolarını görmez. Yalnızca veritabanının nesne görüntüsünü görür; bu nesne görüntüsü, [Hibernate] katmanı tarafından sağlanır. Veritabanı tabloları ile [DAO] katmanı tarafından işlenen nesneler arasındaki köprü, temel olarak iki şekilde kurulur:
- XML türündeki yapılandırma dosyaları aracılığıyla
- kod içindeki Java açıklamalarıyla; bu teknik yalnızca JDK 1.5 sürümünden itibaren kullanılabilir
[Hibernate] katmanı, mümkün olduğunca şeffaf olmayı amaçlayan bir soyutlama katmanıdır. Hedeflenen ideal durum, [DAO] katmanının geliştiricisinin bir veritabanıyla çalıştığını tamamen göz ardı edebilmesidir. İlişkisel dünya ile nesne dünyası arasında köprü kuran yapılandırmayı kendisinin yazmaması durumunda bu mümkündür. Bu köprünün yapılandırılması oldukça hassastır ve belirli bir alıştırma gerektirir.
[4] nesne katmanı, BD'in bir yansıması olarak "kalıcılık bağlamı" olarak adlandırılır. Hibernate’e dayanan bir [DAO] katmanı, kalıcılık bağlamındaki nesneler üzerinde kalıcılık işlemlerini (CRUD, create - read - update - delete) gerçekleştirir; bu işlemler Hibernate tarafından SQL komutlarına dönüştürülür ve JDBC katmanı tarafından yürütülür. Veritabanı sorgulama işlemleri (SQL Select) için, Hibernate, geliştiriciye BD'in kendisini değil, [4] kalıcılık bağlamını sorgulamak için bir HQL dili (Hibernate Query Language) sağlar.
Hibernate popüler olmakla birlikte, ustalaşması karmaşıktır. Genellikle kolay olarak sunulan öğrenme eğrisi, aslında oldukça diktir. Bir-çok veya çok-çok ilişkileri içeren tablolara sahip bir veritabanı söz konusu olduğunda, ilişkisel-nesne köprüsünün yapılandırılması, yeni başlayanlar için pek de kolay değildir. Yapılandırma hataları, uygulamaların performansının düşmesine neden olabilir.
ORM ürünlerinin başarısı karşısında, Java'nın yaratıcısı Sun, Java 5 ile birlikte ortaya çıkan JPA (Java Persistence API) adlı bir spesifikasyon aracılığıyla bir ORM katmanını standartlaştırmaya karar verdi. JPA spesifikasyonu çeşitli ürünler tarafından uygulanmıştır: Hibernate, Toplink, EclipseLink, OpenJpa, .... JPA ile önceki mimari şu hale gelmiştir:
![]() |
[DAO] katmanı artık bir arayüzler kümesi olan JPA spesifikasyonuyla etkileşim halindedir. Geliştirici, bu sayede standardizasyon açısından kazanç sağlamıştır. Önceden, ORM katmanını değiştirdiğinde, belirli bir ORM ile iletişim kurmak üzere yazılmış olan [DAO] katmanını da değiştirmek zorundaydı. Artık, JPA katmanıyla iletişim kuracak bir [DAO] katmanı yazacaktır. Bunu uygulayan ürün ne olursa olsun, [DAO] katmanına sunulan JPA katmanının arayüzü aynı kalır.
Bu belgede, JPA/Hibernate veya JPA/EclipseLink katmanına dayanan bir [DAO] katmanı kullanacağız. Ayrıca, bu katmanları birbirine bağlamak için Spring 2.8 çerçevesini kullanacağız.
![]() |
Spring'in en büyük avantajı, katmanları kod içinde değil, yapılandırma yoluyla birbirine bağlamaya olanak sağlamasıdır. Dolayısıyla, JPA / Hibernate uygulaması, örneğin uygulamanın JPA'i desteklemeyen JDK 1.4 ortamında çalışması nedeniyle JPA içermeyen bir Hibernate uygulamasıyla değiştirilmesi gerekse bile, [DAO] katmanındaki bu uygulama değişikliği, [métier] katmanının kodunu etkilemez. Yalnızca katmanları birbirine bağlayan Spring yapılandırma dosyası değiştirilmelidir.
Java EE 5 ile başka bir çözüm daha mevcuttur: [metier] ve [DAO] katmanlarını EJB3 (Enterprise Java Bean sürüm 3) ile uygulamak:
![]() |
Göreceğiz ki bu çözüm, Spring kullanan çözümden çok da farklı değildir. Java ortamı EE5, Sun Application Server 9.x (Glassfish), Jboss Application Server, Oracle Container for Java (OC4J) gibi "uygulama sunucuları" olarak adlandırılan sunucularda kullanılabilir. Bir uygulama sunucusu, esasen bir web uygulama sunucusudur. Ayrıca, uygulama sunucusu dışında da kullanılabilen "bağımsız" (stand-alone) EE 5 ortamları da mevcuttur. Uygulama sunucusu dışında kullanılabilen ortamlar da mevcuttur. JBoss, EJB3 veya OpenEJB bu tür ortamlara örnektir.
Bir EE5 ortamında, katmanlar EJB (Enterprise Java Bean) adı verilen nesnelerle uygulanır. EE'in önceki sürümlerinde, EJB'lerin (EJB ve 2.x) uygulanması ve test edilmesi zor olduğu ve bazen düşük performans gösterdiği biliniyordu. EJB2.x "entity" ve EJB2.x "session" arasında bir ayrım yapılır. Kısaca, bir EJB2.x "entity", bir veritabanı tablosundaki satırın görüntüsüdür; EJB2.x "session" ise çok katmanlı bir mimaride [metier] katmanlarını uygulamak için kullanılan bir nesnedir, [DAO] katmanlarını uygulamak için kullanılan bir nesnedir. EJB ile uygulanan katmanlara yöneltilen başlıca eleştirilerden biri, bunların yalnızca EJB kapsayıcıları içinde kullanılabilmesidir; bu kapsayıcılar, EE ortamı tarafından sağlanan bir hizmettir. SE (Standard Edition) ortamına kıyasla kurulumu daha karmaşık olan bu ortam, geliştiricinin sık sık test yapmasını engelleyebilir. Bununla birlikte, EJB'lerin sunucuya dağıtımını otomatikleştirerek uygulama sunucusunun kullanımını kolaylaştıran Java geliştirme ortamları mevcuttur: Eclipse, NetBeans, JDeveloper, IntelliJ ve IDEA. Burada NetBeans 6.8 ve GlassFish v3 uygulama sunucusunu kullanacağız.
Spring çerçevesi, EJB2'lerin karmaşıklığına bir tepki olarak ortaya çıkmıştır. Spring, bir SE ortamında, genellikle EE ortamları tarafından sağlanan çok sayıda hizmeti sunar. Böylece, "Veri Kalıcılığı" bölümünde Spring, uygulamaların ihtiyaç duyduğu bağlantı havuzlarını ve işlem yöneticilerini sağlar. Spring'in ortaya çıkışı, birim test kültürünü teşvik etmiştir; bu testler, SE bağlamında EE bağlamına kıyasla daha kolay uygulanabilir hale gelmiştir. Spring, bir uygulamanın katmanlarının klasik Java nesneleriyle (POJO, Plain Old/Ordinary Java Object) uygulanmasına olanak tanır ve bu nesnelerin başka bir bağlamda yeniden kullanılmasını sağlar. Son olarak, Hibernate, EclipseLink, Ibatis gibi kalıcılık araçları başta olmak üzere birçok üçüncü taraf aracını oldukça şeffaf bir şekilde entegre eder.
Java EE5, EJB2 spesifikasyonundaki eksiklikleri gidermek üzere tasarlanmıştır. EJB ve 2.x, EJB3 haline gelmiştir. Bunlar, bir EJB3 konteyneri içindeyken onları özel nesneler haline getiren açıklamalarla etiketlenmiş POJOs'lerdir. Bu kapsayıcı içinde, EJB3, kapsayıcının hizmetlerinden (bağlantı havuzu, işlem yöneticisi vb.) yararlanabilecektir. EJB3 konteynerinin dışında, EJB3 normal bir Java nesnesi haline gelir. EJB anotasyonları göz ardı edilir.
Yukarıda, Spring'i ve bir EJB3 konteynerini, çok katmanlı mimarimizin olası bir altyapısı (çerçeve) olarak gösterdik. İhtiyacımız olan hizmetleri (bağlantı havuzu ve işlem yöneticisi) sağlayacak olan bu altyapıdır.
- Spring ile katmanlar, POJOs nesneleriyle uygulanacaktır. Bu nesneler, POJOs nesnelerine bağımlılık enjeksiyonu yoluyla Spring hizmetlerine (bağlantı havuzu, işlem yöneticisi) erişebilecektir: bu nesneler oluşturulurken Spring, ihtiyaç duyacakları hizmetlere ilişkin referansları bu nesnelere enjekte eder.
- EJB3 konteyneri ile katmanlar, EJB ile uygulanacaktır. EJB3 ile uygulanan katmanlı bir mimari, Spring tarafından örneklendirilen POJO ile uygulanan mimarilerden çok az farklıdır. Aralarında birçok benzerlik bulacağız.
- Son olarak, çok katmanlı bir web uygulaması örneği sunacağız:
![]() |






