Etiket arşivi: hibernate

Extending Spring’s OpenSessionInViewFilter to not open sessions for request to static resources

If you are using OpenSessionInViewFilter in your application, you may be opening sessions for requests which don’t actually do any session-related things, simply accessing to css, javascript or image files. When you get your requests like images, js files with static urls not from servlets, the case will not happen. But for example, if you use JSF and map your “*.jsf” urls to Faces Servlet and filter it with OpenSessionInView filter to avoid lazy loading exceptions in your facelets, JSF fill send requests to your Faces Servlet to load resources like images, css files, js files and you will open session for those request too although not necessary.

To avoid this, it’s an option to extend Spring’s OpenSessionInViewFilter and ignoring requests of that kind. Thank to Spring guys, it’s too much easy.

OpenSessionInViewFilter is extended from OncePerRequestFilter class. OncePerRequestFilter class is implemented the doFilter() method in a Template Method pattern way. It has shouldNotFilter(request) and doInternalFilter(request, response) methods. shouldNotFilter() method is put there to be implemented by subclasses and it simply returns false in OncePerRequestFilter class. In OpenSessionInViewFilter, doInternalFilter is implemented to handle to session-related stuff and shouldNotFilter method is not overrided to filter all the requests and open sessions for them.

So, to avoid not opening sessions for some kinds of requests, its is enough to extend OpenSessionInViewFilter and override the shouldNotFilter() method for the URLs that you don’t want to filter. For the JSF situation, you can write a method like this:

 
public class MobileMeOpenSessionInViewFilter extends OpenSessionInViewFilter {
 
	@Override
	public boolean shouldNotFilter(HttpServletRequest request) {
		return request.getRequestURI().contains("javax.faces.resource");
	}
 
}

If you put this filter to your web.xml instead of OpenSessionInViewFilter, you will not open sessions for the requests like “javax.faces.resource/jsf.js.jsf” no more.

Viva la resistance!

Hibernate “A component cannot hold properties split into 2 different tables” hatası

Bugün Hibernate’den çok güzel bir Exception yedim. Başta Exception biraz ilginç geldi ama, sebebini anlayınca Hibernate’e acayip hak verdim.
@Embeddable ile notlandırdığım bir sınıfı başka bir entity sınıfının içinde @Embedded ile tanımladığımda, Hibernate ayağa kalkarken şu hatayı attı:

“org.hibernate.AnnotationException: A component cannot hold properties split into 2 different tables”

@Embeddable dediğim sınıfın içeriği şuna benzer bişeydi:

@Embeddable
public class Details {
	// ...	
	// primitif alanlar
	// ...
 
	@ManyToOne
	@JoinTable(...)
	private Category category;
 
	// getters & setters
}

Hatanın sebebi, @JoinTable annotation’ı. @JoinTable’ı kullandığımızda, @Embeddable sınıf, embed edilebilirliğini kaybediyor. @Embeddable için javadoc’ta “Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the persistent properties or fields of the embedded object is mapped to the database table for the entity….” diyor. JoinTable’ı kullandığımızda ise, entity’ler arasındaki ilişki, ayrı bir tabloda kurulduğundan, category alanı embed eden sınıfın tablosuna eklenemiyor.

Çözüm @JoinTable yerine ilişkiyi @JoinColumn ile kurmak. @JoinTable yerine @JoinColumn kullandığımızda, embed eden entity’yi category’ye bağlayacak foreign key, entity’nin tablosuna ekleneceğinden, hata durumu oluşmuyor.

Hibernate’de ResultTransformer kullanımı

ResultTransformer, adından da az çok anlaşılacağı gibi, Hibernate sorgularından dönen değerleri bean, map, list gibi nesnelere dönüştürmeye yarıyor.

Ogrenci, Ders, DersKaydi entity’lerimiz olduğunu varsayalım. Ogrenci entity’miz sistemde kayıtlı öğrencileri, Ders entity’si açılan dersleri, DersKaydi entity’miz de Ogrenci ile Ders nesnelerini eşleştiren ve öğrencinin ders kaydına yönelik bilgileri tutan entity olsun.

Yazacağımız sorguyla sadece ihtiyacımız olan <öğrenci adı, kayıt yaptığı ders adı> ikilisini çekmek isteyelim. OgrenciDTO sınıfımızda da ogrenciAdi ve dersAdi alanlarının olduğunu varsayalım.

<öğrenci adı, kayıt yaptığı ders adı> bilgisini çekmek için yazacağımız HQL veya Criteria sorgusu default olarak bize bir Object[] listesi döner. Listenin her bir elemanı olan Object[]‘in ilk elemanı öğrenci adı iken, ikinci elemanı da kayıt yaptığı dersin adı olur. Bu davranışı Criteria veya Query nesnesi üzerinde setResultTransformer(Transformers(Transformers.ALIAS_TO_ENTITY_MAP); çağrısı ile Object[] yerine Map dönecek şekilde değiştirebiliriz. Bu çağrı ile dönen listenin her elemanı Object[] yerine Map olur ve sorgudan dönen  listeden aldığımız map nesnelerinin üzerinde map.get(“ogrenciAdi”); gibi bir çağrı ile öğrenci adı değerine ulaşabiliriz.

Daha da güzeli, sorgudan dönen değerleri doğrudan bir sınıfın alanlarına eşleştirip Hibernate’in bizim için sorgudan dönen değerleri nesnelere dönüştürmesini sağlayabiliriz. Az önce bahsettiğim OgrenciDTO nesnesi üzerinde bunu mesela şöyle yapıyoruz.

public class OgrenciDTO {
  private String ogrenciAdi;
  private String dersAdi;
 
  public OgrenciDTO() { }
 
  // getter, setter metodlar...
}
List ogrencilerinDersleri = s.createCriteria(DersKaydi.class)
  .createAlias("ogrenci", "ogr").createAlias("ders", "drs")
  .setProjection(Projections.projectionList()
                   .add(Projections.property("ogr.ad"), "ogrenciAdi")
                   .add(Projections.property("drs.ad"), "dersAdi")
          )
          .setResultTransformer(Transformers.aliasToBean(OgrenciDTO.class))
          .list();

Gördüğünüz gibi DersKaydi sınıfı üzerinde yarattığımız Criteria‘da öncelikle DersKaydi sınıfının ogrenci ve ders alanlarına alias‘lar atadık. Sonra öğrenci adı ve ders adı değerleri üzerinde Projection yaparak sorgumuzdan yalnızca bu değerlerin dönmesini sağladık ve son olarak yaptığımız .setResultTransformer(Transformers.aliasToBean(OgrenciDTO.class)) çağrısı ile dönen ikilileri üzerinden OgrenciDTO nesneleri yaratılmasını ve değerlerinin atanmasını sağladık.

Hibernate içerisinde bir çok ResultTransformer gerçekleştirimi hazır olarak geliyor. Şu bağlantıdan ResultTransformer gerçekleştirimlerine göz atabilirsiniz.

Bilgiyi HQL ile çekseydik de şöyle yapacaktık:

List ogrencilerinDersleri = s.createQuery(
  "SELECT dk.ogrenci.ad AS ogrenciAdi, dk.ders.ad AS dersAdi" +
  "FROM DersKaydi AS dk")
  .setResultTransformer(Transformers.aliasToBean(OgrenciDTO.class))
  .list();