Hibernate dirty check mekanizması

ümit Samimi
3 min readMay 8, 2021

--

Hibernate, ‘managed’ statüsündeki yani persistence context’e eklemiş durumda olan varlık(entity) nesnelerini kontrol eder. Bir varlık nesnesi load edildiğinde, o varlık nesnesinin tüm propertiy’lerinin bir kopyası oluşturulur. Flush time dediğimiz senkronizasyon anında ise varlık nesnesinin property’’leri ile load edilen nesnenin property’leri eşleştirilir ve farklılık var mı kontrol edilir. Bu işleme “Hibernate Dirty Check” denir.

Bir örnek üzerinden inceleyelim. Post isimli bir varlık nesnemiz olsun ve veritabanında post isimli bir tablomuz olsun.

@Entity
@Table(name="post")
@DynamicUpdate
public class PostEntity {

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String title;
private String summary;
private String path;

// getter, setter, toString ...
}

Şimdi de bir tane post oluşturup, entitymanager ile veritabanına yazma işlemi yapalım.

@Repository
@Transactional
public class PostRepository {

@Autowired
EntityManager entityManager;

public void save(){
PostEntity postEntity = new PostEntity();
postEntity.setSummary("ozet");
postEntity.setTitle("baslik");
entityManager.persist(postEntity);
}
}

PostRepository isimli bean’in save metodunu çağırdığımızda, veritabanına bir insert atar. Sql çıktısı aşağıdaki gibidir.

Hibernate: call next value for hibernate_sequence
Hibernate: insert into post (path, summary, title, id) values (?, ?, ?, ?)

PostEntity.java içerisindeki “GeneratedValue” anatasyonun strateji tipi SEQUENCE olması sebebiyle, önce sequence’den bir sonraki değeri aldı ve sonrasında o sequence değeri ile insert atıldı.

public void update(){
PostEntity postEntity = entityManager
.find(PostEntity.class, 1l);
entityManager.persist(postEntity);
}

Peki ben aynı kaydı veri tabanından çekip, sonra hiçbir alanını değiştirmeden tekrar persist edersem ne olur?

Hibernate: select postentity0_.id as id1_1_0_, postentity0_.path as path2_1_0_, postentity0_.summary as summary3_1_0_, postentity0_.title as title4_1_0_ from post postentity0_ where postentity0_.id=?

Gördüğünüz üzere, herhangi bir update işlemi yapmadı. Sadece select işlemi yapıldı.

public void update(){
PostEntity postEntity = entityManager
.find(PostEntity.class, 1l);
postEntity.setTitle("baslik2");
entityManager.persist(postEntity);
}

Peki ya varlık nesnesinin bir property’sini değiştirmiş olsaydık ne olurdu?

Hibernate: select postentity0_.id as id1_1_0_, postentity0_.path as path2_1_0_, postentity0_.summary as summary3_1_0_, postentity0_.title as title4_1_0_ from post postentity0_ where postentity0_.id=?
Hibernate: update post set title=? where id=?

Gördüğünüz gibi, select işlemi sonrasında update işlemini de yaptı.

Hibernate her defasında, son loaded snapshot ile entity nesnesinin property’lerini kontrol eder. Değişiklik varsa, update işlemini gerçekleştirir. Bu yüzden bu duruma “Hibernate Dirty Check” denir. Aslında bir performans kaybına sebebiyet vermektedir.

Toplamda bu işlemin maliyet hesabı ya da formülü aşağıdaki gibidir.

Buradaki n değerinin karşılığı, managed(persist) varlık nesnesi sayısıdır. P ise, varlık nesnelerinin property sayısı.

Bir entity load edildiğinde, ‘hibernate dirty checking mechanism” ile mevcut entity ile load edilen entity’nin snapshot’ını kıyaslıyor fakat update işlemi yapmayacağımız zaman bu kıyaslama işlemini de kapatabiliriz.

@Transactional(readOnly = true)

İşlem yaptığımız metodu readOnly = true olarak işaretlersek, herhangi bir update işlemi olmayacağı için ‘hibernate dirty check’ işlemi de yapılmaz. Bu da bize performans sağlar.

Eğer spring kullanmıyorsanız, aşağıdaki şekilde de FlushType’ı set edebilirsiniz.

Session session = entityManager.unwrap(Session.class);
session.setDefaultReadOnly(true);

Umarım faydalı olmuştur.

--

--