pvr
pvr

Reputation: 61

Tell Hibernate to ignore property from dirty checking

I have been trying to figure out how to fix this issue from couple of days. It is bad that hibernate did not provide ready to use annotation to exclude a property from dirty checking. Here is the problem, I have a class as below

Class A {
@Column 
Property 1
@Column 
Property 2..etc

@Column
Date lastUpdateDate
@Column
String lastUpdateBy
}

I want to exclude lastUpdateDate and lastUpdateBy from dirty check! Here is what I am doing

  1. using Dynamic-Update = true

  2. Using interceptor and overriding findDirty method to tell hibernate if the object is dirty (here, previousState is always null because the object is transient so no way to compare old values)

  3. merge would fix the issue but it is causing performance problems (few million records ) so I have to use saveOrUpdate which does not pass previousState to Interceptor

Is there any other way to fix this issue?

Upvotes: 6

Views: 4751

Answers (4)

Paweł Dec
Paweł Dec

Reputation: 31

It is possible to ignore specific entity fields from dirty check. You must override DefaultFlushEntityEventListener like this:

@Component
public class CustomFlushEntityEventListener extends DefaultFlushEntityEventListener {

    private static final List<String> IGNORE_DIRTY_CHECK_PROPERTIES = List.of(
        "lastModifiedBy",
        "lastModifiedDate"
    );

    @Override
    protected void dirtyCheck(final FlushEntityEvent event) throws HibernateException {
        super.dirtyCheck(event);
        removeIgnoredDirtyCheckProperties(event);
    }

    private void removeIgnoredDirtyCheckProperties(final FlushEntityEvent event) {
        var propertyNames = event.getEntityEntry().getPersister().getPropertyNames();
        var dirtyProperties = event.getDirtyProperties();
        if(dirtyProperties == null) return;

        var newDirtyProperties = new java.util.ArrayList<Integer>();

        for (int dirtyProperty : dirtyProperties) {
            if (!IGNORE_DIRTY_CHECK_PROPERTIES.contains(propertyNames[dirtyProperty])) {
                newDirtyProperties.add(dirtyProperty);
            }
        }

        var newDirtyPropertiesArray = newDirtyProperties.stream().mapToInt(i -> i).toArray();
        event.setDirtyProperties(newDirtyPropertiesArray.length > 0 ? newDirtyPropertiesArray : null);
    }

}

and then replace listener:

@Component
@RequiredArgsConstructor
public class HibernateListenerConfigurer {

    private final EntityManagerFactory entityManagerFactory;
    private final CustomFlushEntityEventListener customFlushEntityEventListener;

    @PostConstruct
    protected void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        registry.getEventListenerGroup(EventType.FLUSH_ENTITY).clear();
        registry.getEventListenerGroup(EventType.FLUSH_ENTITY).appendListener(customFlushEntityEventListener);
    }

}

You can also replace IGNORE_DIRTY_CHECK_PROPERTIES list with custom annotation on entity property for example @IgnoreDirtyCheck and read it from event.getEntity() by reflection.

Upvotes: 3

A0__oN
A0__oN

Reputation: 9100

You can use @OptimisticLock(excluded = true) to exclude it from dirty checking.

DOCS

Upvotes: 3

benbenw
benbenw

Reputation: 753

You should probably use hibernate / JPA entity listener to update your properties (lastUpdateDate)

https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/listeners.html

it's a standard solution easier to use than implementing your own dirty checking.

Upvotes: 0

Josh Chappelle
Josh Chappelle

Reputation: 1588

Try adding updateable=false to your @Column. It causes that field to not be included in the update statements so I would think it would exclude it from dirty checking.

Check out this link:

@javax.persistence.Column( updatable=false )

Upvotes: -1

Related Questions