ArÅŸiv

‘Hibernate’ kategorisi için arÅŸiv

Hibernate Criteria API – Restrictions.sqlRestriction metodu

Cumartesi, 01 Mar 2014 Yorum yapılmamış

Hibernate Criteria API’de hazırlanmakta olan sql’e “native sql” ekleyebilmemizi saÄŸlayan özel bir metot bulunuyor; Restrictions.sqlRestriction(). Özellikle veritabanı fonksiyonlarından faydalanmak istediÄŸimizde veyahut da düz sql ile daha kolay halledebileceÄŸimizi düşündüğümüz durumlarda kullanışlıdır da.

Fakat bu metodun tek parametre alan hali sorunlara yol açabilir. EÄŸer kullanıcı tarafından(ekrandan, dosyadan vs.) girilen deÄŸer bu sql ifadesine doÄŸrudan parametre olarak ekleniyorsa muhtemel bir “sql injection” veya yapısı bozulan bir sorgu(missing right/left parenthesis, invalid character vs.) ile karşılabilirsiniz.

Bu durumlardan sakınmak için metodun parametrelerinin tipini de belirtebildiğimiz versiyonunu kullanmak gerek.

//...
Criterion criterion = Restrictions.sqlRestriction("NLS_LOWER(USER_NAME, 'nls_sort = XTurkish') like  '" + userName + "'");
//...

ÅŸeklinde parametreyi sql ifadesine doÄŸrudan eklemek yerine

//...
//Tek parametre olmasi durumundaki kullanim
Criterion critCode = Restrictions.sqlRestriction("NLS_LOWER(USER_NAME, 'nls_sort = XTurkish') like ? ", userName, StandardBasicTypes.STRING);
//...
//Birden fazla parametre olmasi durumundaki kullanim
Object[] parameterValues = new Object[]{userName, userSurname};
Type[] parameterTypes = new Type[]{StringType.INSTANCE, StringType.INSTANCE};
criteria.add(Restrictions.sqlRestriction("NLS_LOWER(USER_NAME, 'nls_sort = XTurkish') like ? or NLS_LOWER(USER_SURNAME, 'nls_sort = XTurkish') like ? ", parameterValues, parameterTypes));
//...

şeklindeki kullanım daha doğru olur.

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail

Hibernate Blob Tipi – Criteria API Example uyuÅŸmazlığı

Pazartesi, 10 Ara 2012 Yorum yapılmamış

Geçenlerde epeyce vaktimi alan bir hatayı nasıl çözdüğümüzden bahsedeyim. Olur da benzer bir hata ile cebelleşen birileri arar da denk gelir. Üşenme yaz, kullanıcı bilmezse Google bilir.

Bütün mesele bir dosyayı veritabanında BLOB olarak tanımlanmış bir kolona yazmaktı aslında. Dosya içeriğini byte[] tipinde değil de, java.sql.Blob tipinde tanımlamak gerektiğini öğrendim bu arada, aksi durumda eşlemede tip uyuşmazlığı hatası alınıyor.

Entity sınıfı : File.java

...
private Blob fileContent;
//private byte[] fileContent; 
...

Hibernate eşleme(mapping) dosyası : File.cfg.xml

...
<property name="fileContent" type="blob" column="FILE_CONTENT" />
...

(Hibernate Annotation ile aynı hata oluşuyor mu bilmiyorum bu arada)

şeklinde eşledim dosyanın içeriğini. Fakat testleri çalıştırdığımda

java.sql.SQLSyntaxErrorException: ORA-00932:
inconsistent datatypes: expected – got BLOB

ÅŸeklinde bir hata ile karşılaÅŸtım. Uzunca bir süre debelendikten sonra, basit bir hibernate projesi oluÅŸturup, önce en güncel hibernate kütüphaneleri ile sonra da 3.6 sürümü kütüphaneleri ile test ettim. Herhangi bir sorun olmadan yukarıdaki eÅŸlemelerle dosyayı kaydedebildim. Forumlarda benzer bir sorunla karşılaÅŸmış olanların belirttiÄŸi hibernate.cfg.xml‘de çeÅŸitli ayar deÄŸiÅŸiklikleri yapmak, hibernate ve bağımlı olduÄŸu kütüphanelerin sürümlerini incelemek, debug loglarını satır satır incelemek ve sair derken uzunca bir maratondan sonra oyunun sonunu getirdik, prensesi kurtardık.

Sorunun kaynağı Blob tipi bir alana sahip sınıf için Hibernate Criteria API’nin Example yapısı ile arama yapmak imiÅŸ. Böyle bir durumda yukarıdaki gibi bir tip uyuÅŸmazlığı hatası alınıyor. Çözüm olarak ilk akla gelen Blob tipindeki alanı excludeProperty olarak belirtip, kriter listesinin dışında bırakmaktı tabi ki.

...
File file = new File();
file.setName("XFile");
...
//set other file properties
...
//Create an example to search for
Example example = Example.create(file)
//but exclude the property named "fileContent"
                  .excludeProperty("fileContent");  

List<File> results = session.createCriteria(File.class).add(example).list();
...

Muhtemelen benzer hatayı Clob tipi için de verecektir. Akıllarda buluna, gaflete düşülmeye…

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail

Hibernate dynamic-insert, dynamic-update

Cumartesi, 06 Ağu 2011 Yorum yapılmamış

Hibernate’in bir kullanılabilir özelliÄŸi daha; dynamic-insert, dynamic-update : )

dynamic-update : Güncelleme sql ifadesini(update sql) çalışma zamanında sadece değişen kolonlara göre dinamik olarak oluşturur.
dynamic-insert : Kayıt ekleme sql ifadesini(insert sql) çalışma zamanında sadece veri girilmiş kolonlara göre dinamik olarak oluşturur.

Şöyle ki;

@Entity
@Table(name = "PERSONEL")
@org.hibernate.annotations.Entity(dynamicInsert=true,dynamicUpdate=true)
public class PERSONEL{
...
...
...
}

Sadece AD, SOYAD ve TC_KIMLIK_NO alanlarının girildiği bir kayıt işlemi sonrası

INSERT INTO PERSONEL(AD, SOYAD, PERSONEL_NO, TC_KIMLIK_NO, YAS, CINSIYET, DOGUM_TARIHI)  VALUES (?, ?, ?, ?, ?, ?, ?)

gibi bir sql ifadesi oluşması yerine

INSERT INTO PERSONEL(AD, SOYAD, TC_KIMLIK_NO)  VALUES (?, ?, ?)

gibi sadece veri girilmiş alanların dahil edildiği bir sql ifadesi oluşuyor.

Performans açısından ise şöyle bir ters orantı var. Normal koşullarda Hibernate her varlık sınıfı için üretilen ekleme , güncelleme, sorgulama sql ifadelerini belleğine alır. Böylece her ekleme, güncelleme, sorgulama işlemi için bir sql ifadesi oluşturması gerekmez. Bu performans açısından bir artı getirir. Amma lakin ki bu özellik aktif hale getirildiğinde de her ekleme, güncelleme işlemi için bir sql ifadesi oluşturma işi çıkıyor ortaya. Dolayısıyla çok fazla kolon içeren tablolar için çalıştırılacak sql ifadesini minumuma indirerek performans açısından bir artı getirebilse de diğer durumlar için karar vermek gerekiyor, bu özelliği aktif yapıp yapmamaya.

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail

Hibernate @SqlDelete

Cumartesi, 06 Ağu 2011 Yorum yapılmamış

Hibernate’in yeni keÅŸfettiÄŸim bir özelliÄŸi varlık sınıflarına uygulanabilen @SqlDelete, @SqlUpdate, @SqlInsert bildirimleri. Bu sayede ilgili varlık sınıflarını kaydetme, güncelleme ve silme iÅŸlemleri sırasında hangi sql ifadesinin çalışacağını belirleyebiliyoruz. Bir nevi EntityManager sınıfının persist, merge ve remove metotları sonucu oluÅŸacak olan ekleme, güncelleme ve silme sql ifadelerini ezmiÅŸ oluyoruz. Özellikle silme iÅŸlemi için oldukça faydalı olabilir diye düşündüm.

Mesela; Hesap nesnelerini karşılayan Hesap sınıfları için ne zaman entityManager.remove(herhangiBirHesap) desek, bu nesneyi fiziksel olarak silmek yerine mantıksal olarak durumunu pasif yapmak isteyebiliriz. “Soft delete” olarak bilinen durum yani. Bu gibi durumlar için proje genelinde bir kod tasarrufu ve temizliÄŸi saÄŸlanabilir gibi.

Ayrıca sınıf tanımında kullanılacak @Where bildirimi ile de veritabanından Hesap tablosuna sorgu atan sql ifadelerine bir koşul konulabiliyor. Böylece de durumunu pasif yaptığımız nesneler sorgu sonuçlarında gelmiyor. Bu yazılan sql ifadeleri için hql kullanılamıyor bu arada, sql yazmak gerekiyor.

Şöyle ki;

@Entity
@Table(name = “HESAP”)
@SQLDelete(sql=”update HESAP set DURUM=0 WHERE ID=?”)
@Where(clause=”DURUM <>0″)
public class Hesap {



}

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail

Hibernate @Version – OptimisticLockException

Çarşamba, 29 Ara 2010 Yorum yapılmamış

Hibernate, @Version ile işaretlenen kolon sayesinde bir oturumda açılan varlık nesnesinin, diğer bir oturumda değiştirilmesine izin vermeyerek OptimisticLockException fırlatır. Bu istisna yakalanarak ilgili işlemler yapılabilir. Değişiklik yapılmak istenen varlık nesnesinin başka bir oturumda değiştirildiği belirtilerek kullanıcı yönlendirilebilir örneğin.

@Version ile işaretlenmiş kolon Long, Integer veya TimeStamp tipinde olabilir. Hibernate bu nesne (veritabanında karşılığı olan satır) üzerinde gerçekleştirilen persist() ve sonraki her bir merge() işlemi ile beraber versiyon kolonunun değerini bir arttırır.

@Entity
public class Flight implements Serializable {

@Version
@Column(name=”OPTLOCK”)
private Integer version {

}

}

Şöyle ifade edebiliriz sanırım; Nesnenin veritabanından okudunduÄŸu zamanki versiyonuna ilkVersiyon, yapılan deÄŸiÅŸiklikler veritabanına yansıtılmadan hemen evvelki versiyona sonVersiyon dersek Hibernate API’nin deÄŸiÅŸiklikleri veritabanına yansıtan metodu olan EntityManager.flush() ilkVersiyon.equals(sonVersiyon) kontrolu yapar. Ä°ki versiyon deÄŸeri eÅŸitse deÄŸiÅŸiklikleri veritabanına yansıtır, farklıysa -ki bu ilgili satırın bir baÅŸka oturumda deÄŸiÅŸtirildiÄŸi anlamına gelir- OptimisticLockException fırlatır. Bu durumdaki nesne “stale object” tanımına girer.

BahsettiÄŸim hata;

javax.ejb.EJBException: javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

gibi bir hatadır.

Burada dikkat edilmesi gereken EntityManager sınıfının executeUpdate() metodunun bu versiyon alanının değerini arttırmadığı. Bu yüzden ilgili varlık sınıfları için böylesi bir işlem yapılmadığına dikkat etmek gerekiyor ki neden OptimisticLockException almıyoruz diye debelenmeyelim.

Ve java gurusu abilerimizden Hakan Uygun der ki, bu yöntem ilgili nesneyi barındıran her veritabanı işlemi için (yukarıdaki gibi bir versiyon numarası eşitliği gerçekleştirmek adına) bir sorgu daha çekmek demek. Dolayısıyla bu kolonu öyle ilgili ilgisiz her varlık sınıfına eklememek gerekir. Ayrıca bir çözüm değil, sadece böylesi eş zamanlı düzenleme işlemlerinde alınabilecek hataları engellemek adına uygulanabilecek bir yöntemdir.

Konuyla ilgili daha detaylı bilgi için şuraya bakılabilir.

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail

Object references an unsaved transient instance

Cuma, 07 May 2010 Yorum yapılmamış

Hibernate API ‘yi kullananlar TransientObjectException:Object references an unsaved transient instance – save the transient instance before flushing hatası iÅŸle karşılaÅŸmışlardır mutlaka. Şöyle bir örnek üzerinden açıklayacak olursak; Araba ve Kullanici adli iki modelimiz olduÄŸunu ve Araba modelinde

@ManyToOne()
@JoinColumn(name = “KULLANICI_ID”)
private Kullanici kullanici;

şeklinde bir Kullanici alanımız olduğunu varsayalım. Aksiyon sınıfında;


araba.setKullanici(kullanici);
entityManager.persist(araba);
entityManager.flush();

ÅŸeklinde bir kod bloÄŸu yukarıdaki hatayı almamıza sebep olacaktır. Kullanici nesnesi sessionda varolmasına raÄŸmen henüz veritabaninda mevcut deÄŸildir, yani “transient object” tanımına girmektedir. Araba tablosunda “foreign key” olarak bulunan KULLANICI_ID alanına yerleÅŸtirilecek bir ID yoktur ortalıklarda. Dolayısıyla iliÅŸki kurulamaz. Hatayı iki ÅŸekilde gidermek mümkün. Birincisi kod bloÄŸunu;


araba.setKullanici(kullanici);
entityManager.persist(araba.getKullanici());
entityManager.flush();

entityManager.persist(araba);
entityManager.flush();

şeklinde düzenleyip önce Kullanici nesnesini veritabanına yazmak, ikincisi de ilişkiyi

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = “KULLANICI_ID”)
private Kullanici kullanici;

şeklinde tanımlayarak bu Araba veritabanına kaydedilirken ve güncellenirken Kullanici nesnesinin de veritabanina yazilmasini sağlamak. Elbette ki sağlıklı olan annotation da ilişkinin özelliğini bildirerek bu gibi kontrolleri ve performansı API nin kendisine bırakmak.

Benzer bir durum OneToMany() ilişkilerde de sözkonusu. Çözüm yine benzer şekilde.

FacebooktwitterlinkedinmailFacebooktwitterlinkedinmail