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

Kim Korkar Javadan Enterprise Java Onizleme 9.4.7. PROJE 110.03-com.jdbc.databaseChanging ................... 9.4.8. Transaction ...................................................................... 9.4.9. Rollback ............................

Detaylı