Skip to content

Instantly share code, notes, and snippets.

@mathd
Created November 27, 2017 16:23
Show Gist options
  • Save mathd/d4668078b460bf17187f3893f12f68bb to your computer and use it in GitHub Desktop.
Save mathd/d4668078b460bf17187f3893f12f68bb to your computer and use it in GitHub Desktop.
Hibernate dirty interceptor. Help to find what causes hibernate to flag a table dirty
/* Interceptor */
package net.premieresloges.backend;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.springframework.stereotype.Component;
import net.premieresloges.model.DirtyAware;
import net.premieresloges.model.util.AuditEntity;
@Component
public class MyInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(MyInterceptor.class);
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
EqualsBuilder builder;
if ( entity instanceof AuditEntity ) {
for ( int i=0; i < propertyNames.length; i++ ) {
if (propertyNames != null) {
builder = new EqualsBuilder().append(currentState[i], previousState[i]);
//logger.info(propertyNames[i] + " 1 - " + builder.isEquals());
//logger.info(propertyNames[i] + " 2 - " + EqualsBuilder.reflectionEquals(currentState[i], previousState[i]));
}
}
}
return false;
}
@Override
public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
if(entity instanceof DirtyAware) {
DirtyAware dirtyAware = (DirtyAware) entity;
Set<String> dirtyProperties = dirtyAware.getDirtyProperties();
int[] dirtyPropertiesIndices = new int[dirtyProperties.size()];
List<String> propertyNamesList = Arrays.asList(propertyNames);
int i = 0;
for(String dirtyProperty : dirtyProperties) {
logger.info("The {} property is dirty " + dirtyProperty);
dirtyPropertiesIndices[i++] = propertyNamesList.indexOf(dirtyProperty);
}
dirtyAware.clearDirtyProperties();
return dirtyPropertiesIndices;
}
return super.findDirty(entity, id, currentState, previousState, propertyNames, types);
}
}
/* DirtyAware interface */
package net.premieresloges.model;
import java.util.Set;
public interface DirtyAware {
Set<String> getDirtyProperties();
void clearDirtyProperties();
}
/* ApplicationContext.xml */
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource" /></property>
<property name="packagesToScan" value="net.premieresloges.model"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
<prop key="hibernate.default_schema">public</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernateHbm2ddl}</prop>
<!--<prop key="hibernate.default_batch_fetch_size">1000</prop>-->
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.cache.use_structured_entries">false</prop>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.id.optimizer.pooled.prefer_lo">true</prop>
</props>
</property>
<property name="entityInterceptor"><ref bean="myInterceptor"/></property>
</bean>
<bean id="myInterceptor" class="net.premieresloges.backend.MyInterceptor" />
/* Self dirty checking entity */
package net.premieresloges.model;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.Transient;
import net.premieresloges.model.util.AuditEntity;
public class SelfDirtyCheckingEntity extends AuditEntity implements DirtyAware {
private static final long serialVersionUID = 1L;
private final Map<String, String> setterToPropertyMap = new HashMap<String, String>();
@Transient
private Set<String> dirtyProperties = new LinkedHashSet<String>();
public SelfDirtyCheckingEntity() {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
Method setter = descriptor.getWriteMethod();
if (setter != null) {
setterToPropertyMap.put(setter.getName(), descriptor.getName());
}
}
} catch (IntrospectionException e) {
throw new IllegalStateException(e);
}
}
@Override
public Set<String> getDirtyProperties() {
return dirtyProperties;
}
@Override
public void clearDirtyProperties() {
dirtyProperties.clear();
}
protected void markDirtyProperty() {
String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
dirtyProperties.add(setterToPropertyMap.get(methodName));
}
}
/* Class that implements SelfDirtyCheck */
@Entity
@Table(name = "subscription")
public class Subscription extends SelfDirtyCheckEntity implements ToIdString, Serializable, Activable, AccountEntity, Comparable<Subscription> {
static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(Subscription.class);
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment