Spring ve Hibernate Entegrasyonu
Transkript
Spring ve Hibernate Entegrasyonu
Spring ve Hibernate Entegrasyonu Spring Application Framework ve Hibernate ORM Framework'ün doğuşu hemen hemen aynı dönemlere rastlar. Her iki framework'de EJB spesifikasyonu etrafında şekillenen hantal kurumsal Java programlama modeline bir nevi baş kaldırı olarak ortaya çıkmışlar ve gelişim süreçlerinde kol kola yürümüşlerdir. İlk Zamanlar S pring Application Framework kurumsal Java projeleri geliştirmek için ille de monolitik bir uygulama sunucusuna ihtiyaç olmadığını, J2EE ve EJB spesifikasyonlarının vaaz ettiği programlama modelinin tek yol olarak görülemeyeceğini söylüyor, transaction yönetiminin uygulama sunucuları dışında da rahatlıkla gerçekleştirilebileceğini savunuyor ve POJO tabanlı bir programlama modelini tekrar gündemimize sokuyordu. “Template” yapılarıdır. Hibernate entegrasyonu çerçevesinde de bu sınıfların karşılıkları HibernateDaoSupport ve HibernateTemplate olarak tanımlanmıştır. Hibernate tarafındaki gelişmeler sonucunda artık bu yapılar “legacy” olarak tabir edilmektedir. Ancak Spring ve Hibernate entegrasyonunun ana kısımlarının anlaşılabilmesi ve mevcut uygulamalarda da halihazırda bu yapıların kullanılmasından ötürü bu bölümde iki yapı üzerinde duracağız. HibernateDaoSupport Spring'in veri erişim katmanı oluşturmak için DAO örüntüsü üzerine kurulu DaoSupport sınıflarından birisi de HibernateDaoSupport üst sınıfıdır. Bu sınıfın sunduğu setSessionFactory(...) metodu ile uygulamanın konfigürasyonu sırasında ilgili DAO nesnesine SessionFactory nesnesi enjekte edilir. Daha sonra uygulama içerisinden Spring Application Framework'ün geliştiricileri HibernateDaoSupport sınıfından türeyen bu alt kurumsal Java teknolojilerinde her şeyi sıfırdan sınıfların nesnelerinde SessionFactory ve HibernateTemplate nesnelerine kendilerinin geliştirmesinin yerine, her getSessionFactory() ve getHibernateTemplate() katmanda öne çıkan ve kendi felsefelerine metotları ile erişilebilmektedir. uygun mimari ortaya koymaya imkan veren çözümleri, framework ve kütüphaneleri bir araya getirmeyi, kurumsal Java public class ProductDaoImpl extends spesifikasyonlarının bıraktığı boşlukları HibernateDaoSupport implements ProductDao { dolduran bir “iskelet çözüm” olmayı hedeflemişlerdir. Bu doğrultuda da veri erişim public Collection katmanında hantal “entity bean”lere alternatif olarak POJO tabanlı Hibernate'in kullanılmasını loadProductsByCategory(String category) throws DataAccessException { kolaylaştıracak çözümleri ilk sürümlerinden return itibaren biz geliştiricilere sunmuşlardır. getHibernateTemplate().find( "from Product p where Hibernate 2 ve Spring p.category=?", category); } Spring, uygulamaların veri erişim ihtiyaçları için } iki temel yöntem sunmaktadır. Bunlardan birisi veri erişim katmanını DAO tasarım örüntüsü ile iş mantığı katmanından soyutlamak ve bunun HibernateDaoSupport üst sınıfı, veri erişim için sağlanan “DaoSupport” sınıflarıdır. Diğeri işlemleri sırasında halihazırdaki Hibernate Session nesnesine erişim sunarak işlem ise kullanılan veri erişim teknolojisinin yapılmasını, ayrıca DAO nesnesinin “plumbing” olarak tabir edilen kısımlarının uygulama geliştiricilerin omuzlarından alınarak, metotlarında meydana gelen exception'ların Spring'in genel DataAccessException sadece veri manipülasyon ve sorgulama hiyerarşisine dönüştürülerek ele alınmasını işlemleri ile dönen sonuçların üzerinde işlem sağlar. DAO nesnesi içerisinde aktif Session yapan kısımların kodlanmasını sağlayan nesnesine erişmek için getSession(...) metodu Hibernate'de hemen aynı dönemde sivrilmeye başlıyor ve nesne dünyası ile ilişkisel dünya arasındaki uyumsuzluğu gidermek için ortaya konan EJB spesifikasyonundaki “entity bean” modelinin aksine POJO tabanlı bir yöntemle, sıradan Java nesneleri ile de “persistence” ihtiyacının karşılanabileceğini söylüyordu. kullanılmaktadır. Bu metoda verilen biçimde açılıp, kapatılması, otomatik biçimde “allowCreate” boolean parametresi ile Session Spring'in transaction altyapısına müdahil nesnesinin yaratılması kontrol edilir. Bu metoda olmasını sağlamaktadır. HibernateTemplate geçirilen parametrenin değeri genellikle “false” nesneleri tamamen thread-safe olup, yeniden olacaktır. Bu sayede halihazırda Spring'in kullanılabilir nesnelerdir. Bu nedenle Spring oluşturduğu transaction ile ilişkili bir Session ApplicationContext genelinde tek bir nesnesinin kullanılması sağlanır. HibernateTemplate nesnesi yeterli olmaktadır. HibernateDaoSupport sınıfı ile sunulan metotların benzerleri SessionFactoryUtils SessionFactory Konfigürasyonu sınıfından da erişilebilir. Spring programlama modelinin temel HibernateDaoSupport veya SessonFactoryUtils özelliklerinden birisi uygulama kodu içerisinde üzerinden yapılan veri erişim işlemlerinin artısı, ihtiyaç duyulan kaynaklara, JNDI lookup gibi kod içerisinde doğrudan lookup yapılmamasıdır. veri erişim metotları içerisinden checked Veri erişim işlemleri için gerekli olan JDBC exception'ların fırlatılabilmesidir. Aşağıda DataSource ve Hibernate SessionFactory anlatacağımız HibernateTemplate içerisindeki nesneleri Spring container içerisinde bean callback sınıflarından ise sadece olarak tanımlanır ve uygulama koduna RuntimeException türevi exception'lar fırlatılabilmektedir. Aslında checked exception doğrudan enjekte edilirler. fırlatmanın gün geçtikçe demode hale gelmesi <bean id="dataSource" ile buna tam olarak bir artı özellik demek de class="com.mchange.v2.c3p0.ComboPooledD mümkün değildir. ataSource"> <property name="driverClass" HibernateTemplate value="org.hsqldb.jdbcDriver"/> <property name="jdbcUrl" HibernateTemplate sınıfı ise, Hibernate'in value="jdbc:hsqldb:hsql://localhost"/> Session arayüzünden sunduğu pek çok <property name="user" value="sa"/> metodun benzerini ve bu arayüzde olmayıp <property name="password" value=""/> ORM işlemlerinde geliştiricilerin sıklıkla ihtiyaç </bean> duydukları diğer bazı metotları sunmaktadır. Bu sınıf ile hedeflenen Hibernate Session'a erişmeye ihtiyaç duymadan ORM işlemlerinin yapılmasıdır. Ancak HibernateTemplate tarafından sunulmayan metotlara ihtiyaç duyulması ve doğrudan Session nesnesi üzerinde işlem yapılması gerektiğinde “callback” yöntemi ile Hibernate Session nesnesine erişim sağlanmaktadır. public class ProductDaoImpl implements ProductDao { private HibernateTemplate ht; public void setSessionFactory(SessionFactory sessionFactory) { ht = new HibernateTemplate(sessionFactory); } <bean id="sessionFactory" class="org.springframework.orm.hibernat e3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.di alect.HSQLDialect </value> </property> </bean> Hibernate ile çalışırken SessionFactory nesnesinin JNDI context'e bağlanması söz konusu olabilir. Böyle bir durumda JNDI public Collection lokasyonlu SessionFactory nesnesi Spring'in loadProductsByCategory(String category) JndiObjectFactoryBean factory bean'ı veya throws DataAccessException { Spring 2.0 ile birlikte gelen <jee:jndi-lookup> return ht.find("from Product p namespace konfigürasyonu vasıtası ile where p.category=?", category); uygulama koduna sunulabilir. Ancak JNDI } lokasyonlu SessionFactory kullanımı EJB'lerle } çalışıldığı durumların dışında çok yaygın HibernateTemplate Session nesnelerinin uygun değildir. public interface ProductService { <beans> public void <jee:jndi-lookup id="dataSource" jndi-increasePriceOfAllProductsInCategory(Str name="java:comp/env/jdbc/ds"/> ing category); </beans> public List<Product> findAllProducts(); } Spring ile çalışırken container tarafından yönetilen JNDI lokasyonlu SessionFactory ile public class ProductServiceImpl uygulama içerisinde yerel olarak yönetilen implements ProductService { SessionFactory nesneleri arasında geçiş tek satır kod yazmadan konfigüratif olarak private ProductDao productDao; gerçekleştirilebilmektedir. Burada SessionFactory'nin nerede tutulacağı tamamen public void setProductDao(ProductDao o anda kullanılan transaction stratejisine göre productDao) { belirlenmelidir. this.productDao = productDao; } JNDI lokasyonu SessionFactory ile Spring tarafında tanımlı yerel SessionFactory arasında @Transactional bir karşılaştırma yapıldığında, JNDI public void SessionFactory'nin çok büyük bir artısı increasePriceOfAllProductsInCategory(fin görülemez. Yalnız Hibernate JCA kullanılırsa, al String category) { SessionFactory'nin Java EE sunucusunun List productsToChange = yönetim altyapısı üzerinden idare edilebilmesi productDao. mümkün olmaktadır. Ancak bunun ötesinde bir loadProductsByCategory(category); // ... artı söz konusu değildir. } Spring Transaction Yönetimi ve Hibernate @Transactional(readOnly = true) public List<Product> findAllProducts() { HibernateTransactionManager, return JtaTransactionManager gibi Spring'in hangi productDao.findAllProducts(); transaction stratejisi kullanılsa, ya da doğrudan } EJB CMT veya JTA ile çalışılsa bile SessionFactory.getCurrentSession() metodu o } anda container (bu container Spring olabilir, JEE container olabilir) tarafından yönetilen Burada servis katmanındaki metotlar transactional Session nesnesini döner. Spring'in @Transactional annotasyonu ile işaretlenmiştir. LocalSessionFactoryBean'ı da bu çalışma şekli Spring container, çalışma zamanında bu ile tamamen uyumludur. annotasyonları tespit eder ve ilgili metotlar için transaction kabiliyetini sağlar. Annotasyon Spring ile çalışılırken önerilen transaction tabanlı konfigürasyon XML tabanlı demarcation yöntemi dekleratiftir. Bu sayede transaction demarcation API çağrıları Java kodu konfigürasyona göre çok daha basit ve anlaşılırdır. Yapılması gereken Spring container yerine AOP transaction interceptor içerisinde içerisinde bir TransactionManager bean'ı toplanır. Dekleratif transaction kabiliyeti tanımlamak ve <tx:annotation-driven> XML sayesinde iş mantığı katmanındaki nesneler elemanını koymaktan ibarettir. transaction demarcation API çağrılarından arındırılmış olur, geliştiriciler sadece iş mantığı <bean id="transactionManager" geliştirmeye odaklanabilirler. AOP transaction class="org.springframework.or interceptor'ün konfigürasyonu Java m.hibernate3.HibernateTransactionManage annotasyonları veya XML ile yapılabilmektedir. r"> Transaction yönetimi ile ilgili propagation, <property name="sessionFactory" izolasyon düzeyi gibi davranışlarda ref="sessionFactory"/> konfigürasyon dosyası içerisinden değiştirilebilir. </bean> Aşağıda attribute tabanlı konfigürasyon örneği <tx:annotation-driven/> görülmektedir. <bean id="productService" class="com.speedyframework.service.Prod uctServiceImpl"> <property name="productDao" ref="productDao"/> </bean> gerektirir. Tabiki bu tür bir deployment için öncelikle container'ın JCA desteğinin de olması şarttır. Örneğin WebLogic Express, JCA desteği sağlamaz. Bunun için container'ın kurumsal sürümüne sahip olmanız gerekebilir. Yerel kaynakları kullanan ve yerel transactionlarla tek veritabanına erişerek çalışan Spring Hem programatik transaction demarcation uygulamaları ise herhangi bir web container'a yapılmasını sağlayan TransactionTemplate, hem deploy edilebilir. Buna ilave olarak bu tür bir de dekleratif transaction demarcation'ı uygulamaya ait konfigürasyon rahatlıkla mümkün kılan TransactionInterceptor container dışı ortamlarda, örneğin desktop sınıflarının her ikisi de asıl transaction yönetim veya birim test ortamlarında da çalıştırılabilir. işini PlatformTransactionManager nesnesine Bütün bunları değerlendirdikten sonra havale ederler. Çalışma zamanında konfigüre söyleyebileceğimiz, eğer EJB kullanmıyor iseniz edilen PlatformTransactionManager yerel SessionFactory ve Spring'in transaction implementasyonu stratejilerinden HibernateTransactionManager HibernateTransactionManager veya veya JtaTransactionManager ile çalışmak en JtaTransactionManager olabilir. Hatta kolay ve hızlı yoldur. uygulamaya özel bir PlatformTransactionManager implement etmek bile mümkündür. Nihayetinde yapılması gereken, uygun stratejinin Spring container'a Hibernate'in Gelişim Süreci belirtilmesinden ibarettir. Bu şekilde Hibernate sürüm 2'den sürüm 3'e geçerken konfigüratif yapı sayesinde native Hibernate önemli mimarisel değişikliklere uğramıştır. transaction yönetiminden JTA tabanlı dağıtık Hibernate 2 ile çalışanların temel transaction yönetimine geçiş sadece problemlerinden birisi aynı Hibernate Session konfigürasyon ile halledilebilir bir durumdur. nesnesinin bir transaction boyunca nasıl Böyle bir değişiklik sonrasında transaction yönetileceği idi. Transaction'lar genel olarak demarcation işlemleri ve veri erişim kodları hiç Java thread context'inde yönetildiği için problemsiz çalışacaktır. Hibernate Session nesnesinin de benzer bir HibernateTransactionManager, Hibernate JDBC context'de yönetilmesi en mantıklı olanıydı. Ancak Hibernate 2 içerisinde bununla ilgili bir Connection nesnesini doğrudan JDBC işlemlerinde kullanılması için dışarıya erişilebilir kabiliyet olmadığı için geliştiriciler kendilerine kılar. Bunun için ya SessionFactory nesnesinin özel çözümler geliştirmek zorunda kalmışlardır. İşte bu noktada Spring'in transaction boyunca LocalSessionFactoryBean'ın dataSource tek bir Hibernate Session nesnesinin property'sine DataSource nesnesi verilerek kullanılmasını sağlayan altyapısı Spring ve oluşturulması gerekir, ya da Hibernate uygulamalarının yaygınlaşmasında HibernateTransactionManager'ın dataSource önemli bir katalizör görevi görmüştür. property'sine ilgili DataSource bean set Hibernate 3 oluşturulurken Session nesnesinin edilerek hangi DataSource nesnesi için transaction senkronizasyonu yapacağı söylenir. değişik context'lerde yönetilmesi pluggable bir mimari üzerine bina edilmiştir. Bunlardan en Bu sayede Hibernate ORM işlemleri ile yaygın olanı Session nesnesinin web isteği doğrudan JDBC API ile yapılan veri erişim boyunca bir ThreadLocal değişken içerisinde işlemleri aynı transaction yönetimi içerisinde tutularak yönetildiği yapıdır. gerçekleştirilmiş olunur. Böylece tek bir veritabanının kullanıldığı durumlarda Hibernate Hibernate 2'nin en çok eleştiri aldığı diğer bir ve JDBC API erişimlerinin transaction senkronizasyonu için JTA kullanmaya da gerek nokta ise fırlattığı exception'ların hepsinin “checked exception” olmasıydı. Veri erişim kalmaz. işlemleri sırasında ortaya çıkan exception'ların Eğer tek bir veritabanına erişim söz konusu ise pek azı uygulama tarafından ele alınabilir, pek yerel olarak tanımlanmış bir JDBC DataSource çoğu ise “fatal” olarak kabul edilir. Fırlatılan ve Hibernate SessionFactory nesneleri Spring checked exception'ların, metodu çağıran tarafından yönetilen transactionların çalışması istemci kod tarafında ya ele alınması ya da için yeterlidir. Spring'in JTA transaction istemci kodunun bu exception'ları bir üst stratejisine ise ancak dağıtık transaction katman delege ettiğini deklere etmek zorunda ihtiyaçları söz konusu olduğunda kalması bazı kötü kodlama pratiklerine yol başvurulmalıdır. JCA üzerinden bir açmakadır. Bu nedenle checked exception'lara konfigürasyon ise container'lara özel Java programcıları pek iyi gözle deployment adımlarının uygulanmasını bakmamaktadır. Spring programlama modeli ise veri erişim hatalarını DataAccessException aynıdır. Buradaki tek fark Spring tarafında üst sınıfı ile RuntimeException sınıf yönetilen DAO nesnesinde ki SessionFactory'nin hiyerarşisinden türeterek ortak bir exception bir instance değişkeninde tutulmasıdır. hiyerarşisine dönüştürmektedir. Bunun yanında SessionFactory'nin Hibernate referans da RuntimeException kullanarak ortaya çıkan dokümantasyonu örneklerindeki gibi hataların uygulama tarafında her ne zaman ele HibernateUtil gibi bir yardımcı sınıf üzerinden alınabilir ise o zaman try/catch blokları ile ele uygulama genelinde statik olarak paylaşılması alınmasını sağlamıştır. Hibernate sürüm 3 ile çok da tercih edilmemesi gereken bir exception hiyerarşisinde de bir değişikliğe yaklaşımdır. Genel bir ilke olarak da, gitmiş, HibernateException üst sınıfını gerekmedikçe statik değişkenlerin kullanılması RuntimeException'dan türer hale getirerek veri önerilen bir şey değildir. erişim hatalarını istendiği vakit ele alınabilir kılmıştır. Bu tür DAO nesnesinin temel avantajı sadece Hibernate 3 API'sine bağımlı olmasıdır. Hibernate 2 ile çalışırken Spring Hibernate3 ve Spring HibernateTemplate'ın kullanılması haklı Yukarıda da belirttiğimiz gibi Hibernate 3 ile nedenlerle yaygın bir pratik olmuştu, ancak birlikte contextual session desteği gelmektedir. Hibernate 3 ile birlikte, Spring API'sine bağlı Bu sayede Hibernate, bir transaction boyunca kalmadan DAO katmanını geliştirmek isteyen tek bir Session nesnesini yönetir ve veri erişim Hibernate kullanıcıları için bu tür bir kodlama işlemlerinde kullanılmasını sağlar. Bu sayede daha uygundur. HibernateTemplate ve HibernateDaoSupport Ancak burada da uygulamanın Hibernate ile çok gibi aracı sınıflara ihtiyaç duymadan düz içli dışlı olması durumu ortaya çıkmaktadır. Hibernate 3 API'si kullanarak DAO implementasyonları geliştirmek daha kolay hale Özellikle fırlatılan exceptionların detayından uygulama davranışının belirlenmesi söz konusu gelmiştir. olduğunda, HibernateException'ın spesifik alt public class ProductDaoImpl implements sınıflarına kadar bir bağımlılık söz konusudur. Bu noktada da karşımıza veri erişim ProductDao { katmanında Java Persistence API gibi uygulamayı ORM implementasyonlarından private SessionFactory sf; soyutlamayı hedefleyen bir yapı çıkmaktadır. public void Ancak şu an için pratikte bu soyutlama pek setSessionFactory(SessionFactory sf) { gerçekleştirilebilir durumda değildir. this.sf = sf; Kısacası Hibernate 3 ile birlikte DAO } implementasyonları oldukça kolaylaşmış, Spring'in HibernateTemplate gibi bir yardımcı public Collection loadProductsByCategory(String category) sınıfına ihtiyaç neredeyse ortadan kalkmıştır. Ancak, veri erişim yöntemlerinin fırlattığı { exceptionları Spring'in kendi genel veri erişim return sf.getCurrentSession() exception hiyerarşisine çevrilmesi ve .createQuery("from Product p where p.category = ?").setParameter(0, transaction yönetiminde Spring transactionların kullanılmasının artıları hala sürmektedir. category).list(); } } Sonuç Her ne kadar iki framework'ün geliştirici grupları arasında zaman zaman söz düelloları yaşansa da bizim kanaatimiz, her iki framework'ün de daha uzun bir süre etle tırnak gibi bir arada kullanılmaya devam edeceği yönündedir. Kış uykusundan çıkıp bahara gireli çok oldu, hatta bu yazı yayımlandığında yazın ortalarına gelmiş olacağız. Herkese Spring ve Yukarıdaki örnek Hibernate referans dokümantasyonu ve örnekleri ile hemen hemen Hibernate ile bol Java'lı günler dileriz. <bean id="productDao" class="com.speedyframework.dao.ProductD aoImpl"> <property name="sessionFactory" ref="sessionFactory"/> </bean> Yazar: ODTÜ Bilgisayar Mühendisliği'nden 1999 yılında mezun olan Kenan Sevindik o dönemden bu yana pek çok büyük ölçekli kurumsal yazılım projesinde görev yapmıştır. Halihazırda kurumsal Java teknolojileri ile yazılım geliştirme, eğitim ve danışmanlık hizmeti sunan yazarımız, değişik ortamlarda teknoloji içerikli konuşma ve sunumlar da yapmaktadır. Edindiği bilgi birikimi ve deneyimleri http://www.harezmi.com.tr/blog adresinden sanal alemde sizlerle paylaşmaktadır. Kendisi ile iletişime geçmek için [email protected] adresine mesaj gönderebilirsiniz.
Benzer belgeler
Kim Korkar Javadan Enterprise Java Onizleme
9.4.7. PROJE 110.03-com.jdbc.databaseChanging ...................
9.4.8. Transaction ......................................................................
9.4.9. Rollback ............................