Panadol Chong
Panadol Chong

Reputation: 1903

how to avoid dirty checking in hibernate

I have a entity call Company, and another entity call token.

1 Company will have many tokens.

So in my Company entity, I will have something like follow:

 @OneToMany(mappedBy = "companyId")
 public Set< Token > getTokens() {
    return tokens;
 }

However, I will have some logic during the return, it will change the token list before return, its something like follow:

@OneToMany(mappedBy = "companyId")
 public Set< Token > getTokens() {
    tokens.remove( token );
    return tokens;
 }

Because of I changing the value in token list, thus, every time I select the company object from db using hibernate, the company table will be updated automatically.

Base on my understanding, this is a behavior of hibernate dirty checking. So when Hibernate detect that there something changes on it, it will do the update to database.

Is there any way that can avoid this? For example maybe just call existing hibernate function, so that hibernate will know the token list is dirty, and it will no do the update.

Yes, I know the filtering logic in the getTokens() is not suitable. By right filtering logic should not apply in entity level. But because of currently there is many place using this method, if I change in this entity level, then there will impact others and if I change other place as well, it will need to retest whole application again.

Thus I am trying to find a better way on this.

Upvotes: 1

Views: 8742

Answers (1)

Anastasios Vlasopoulos
Anastasios Vlasopoulos

Reputation: 1802

What you actually want is to prevent Hibernate from auto flushing.

See hibernate docs:

Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.

A solution to this problem is to change the default configuration of FlushMode from auto to manual by setting FlushMode.MANUAL. In this way the dirty check mechanism will stop causing the aforementioned synchronization.

Although the Session is only ever flushed when Session.flush() is explicitly called by the application as described in the hibernate documentation.

Here it has to be mentioned that if you follow the suggested solution you need to pay the price of the explicitly flushing which means that you need to explicitly call the Session.flush() method every time you want to commit something on the database.

Read here some useful material regarding FlushMode as were officially documented in hibernate documentation.

UPDATE:
This solution applies on the Session, so if you want to apply this only on the specific entity you should try to create two methods. One for setting it to MANUAL and one for back to the default AUTO. In this way you will change your value after MANUAL setting to prevent flushing and subsequently you will set it back to the default in order not to affect the other entities.
Maybe the below implementation will help:

@Autowired
private SessionFactory sessionFactory;


@OneToMany(mappedBy = "companyId")
 public Set< Token > getTokens() {
    setFlushModeManual();
    tokens.remove( token );
    setFlushModeAuto();
    return tokens;
 }

private void setFlushModeManual() {
    sessionFactory.getCurrentSession().setFlushMode(FlushMode.MANUAL);
}

private void setFlushModeAuto() {
    sessionFactory.getCurrentSession().setFlushMode(FlushMode.AUTO);
}

Upvotes: 3

Related Questions