Reputation: 61
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
using Dynamic-Update = true
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)
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
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
Reputation: 9100
You can use @OptimisticLock(excluded = true)
to exclude it from dirty checking.
Upvotes: 3
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
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