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.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

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…

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

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.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

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 {



}

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

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.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

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.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail