spring + hibernate 设置更新指定字段
spring + hibernate 设置更新指定字段
hibernate的update方式默认的是更新所有字段的,这导致一个很大的问题,当想更新部分字段时必须要把整个entity先load一遍,然后set相应的更新字段再保存
这一步load数据势必将数据库的操作增加一倍,而且基本上是多余的,最近看了下hibernate源码,发现hibernate有设置指定更新的方式,具体方式如下
org.hibernate.persister.entity.AbstractEntityPersister中定义了基类的更新方法public void update(final Serializable id, final Object[] fields, final int[] dirtyFields, final boolean hasDirtyCollection, final Object[] oldFields, final Object oldVersion, final Object object, final Object rowId, final SessionImplementor session) throws HibernateException {if ( ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) ) {// We need to generate the UPDATE SQL when dynamic-update="true"propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );// don't need to check laziness (dirty checking algorithm handles that)updateStrings = new String[span];for ( int j = 0; j < span; j++ ) {updateStrings[j] = tableUpdateNeeded[j] ?generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :null;}}else if ( ! isModifiableEntity( entry ) ) {// We need to generate UPDATE SQL when a non-modifiable entity (e.g., read-only or immutable)// needs:// - to have references to transient entities set to null before being deleted// - to have version incremented do to a "dirty" association// If dirtyFields == null, then that means that there are no dirty properties to// to be updated; an empty array for the dirty fields needs to be passed to// getPropertiesToUpdate() instead of null.propsToUpdate = getPropertiesToUpdate(( dirtyFields == null ? ArrayHelper.EMPTY_INT_ARRAY : dirtyFields ),hasDirtyCollection);// don't need to check laziness (dirty checking algorithm handles that)updateStrings = new String[span];for ( int j = 0; j < span; j++ ) {updateStrings[j] = tableUpdateNeeded[j] ?generateUpdateString( propsToUpdate, j, oldFields, j == 0 && rowId != null ) :null;}}else {// For the case of dynamic-update="false", or no snapshot, we use the static SQLupdateStrings = getUpdateStrings(rowId != null,hasUninitializedLazyProperties( object ));propsToUpdate = getPropertyUpdateability( object );}int[] dirtyProperties = session.getInterceptor().findDirty(entity,id,values,loadedState,persister.getPropertyNames(),persister.getPropertyTypes());
package net.esj.basic.dao.hibernate.sessioninterceptor;import java.io.Serializable;import java.util.HashSet;import java.util.Set;import net.esj.basic.pojo.AbstractPojo;import net.esj.basic.pojo.UpdateType;import net.esj.basic.pojo.Updater;import net.esj.basic.utils.JavaProtoTypeHelper;import org.hibernate.EmptyInterceptor;import org.hibernate.type.Type;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Component("dirtyFindInterceptor")@Scope("prototype")public class DirtyFindInterceptor extends EmptyInterceptor {@Overridepublic int[] findDirty(Object entity, Serializable id,Object[] currentState, Object[] previousState,String[] propertyNames, Type[] types) {if(entity instanceof AbstractPojo){Updater updater = ((AbstractPojo)entity).getUpdater();UpdateType type =((AbstractPojo)entity).getUpdateType();if(type==UpdateType.AUTO){type = checkUpdateType(entity, id, currentState, previousState, propertyNames, types);}switch(type){case BY_DIFF:return findDirtyByDiff(entity, id, currentState, previousState, propertyNames, types);case BY_UPDATER:return findDirtyByUpdater(entity, id, currentState, previousState, propertyNames, types, ((AbstractPojo)entity));case NONE:return super.findDirty(entity, id, currentState, previousState, propertyNames,types);}}return super.findDirty(entity, id, currentState, previousState, propertyNames,types);}protected UpdateType checkUpdateType(Object entity, Serializable id,Object[] currentState, Object[] previousState,String[] propertyNames, Type[] types){if(! (entity instanceof AbstractPojo)){return UpdateType.NONE;}Updater updater = ((AbstractPojo)entity).getUpdater();if(updater.hasProperty()){return UpdateType.BY_UPDATER;}return UpdateType.NONE;} private int[] findDirtyByUpdater(Object entity, Serializable id,Object[] currentState, Object[] previousState,String[] propertyNames, Type[] types,AbstractPojo pojo){Updater updater = pojo.getUpdater();Set<Integer> tmp = new HashSet<Integer>(); //int[] reval = new int[propertyNames.length];for(int i=0;i<propertyNames.length;i++){String propertyName = propertyNames[i];if(updater.getProperties().contains(propertyName)){tmp.add(i);}}int[] reval = new int[tmp.size()];int i=0;for(Integer t:tmp){reval[i] = t;i++;}return reval;}private int[] findDirtyByDiff(Object entity, Serializable id,Object[] currentState, Object[] previousState,String[] propertyNames, Type[] types){if(currentState==null|| previousState ==null){return null;}Set<Integer> tmp = new HashSet<Integer>(); for(int i=0;i<currentState.length;i++){Object cur = currentState[i];Object pre = previousState[i];if(!JavaProtoTypeHelper.equal(cur, pre)){tmp.add(i);}}int[] reval = new int[tmp.size()];int i=0;for(Integer t:tmp){reval[i] = t;i++;}return reval;}}<bean id="sessionFactory"/></property><property name="mappingDirectoryLocations"><list><value>classpath*:/net/esj/test/pojo/</value></list></property><property name="hibernateProperties"><props><prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop></props></property><property name="entityInterceptor" ref="dirtyFindInterceptor" /></bean><hibernate-mapping package="net.esj.test.pojo"><classname="Foo"table="test_foo" dynamic-update="true" //dynamic-update="true"此须增加><meta attribute="sync-DAO">true</meta><idname="id"type="string"column="id"><generator name="code">public void testUpdate(){Foo foo = new Foo();foo.setId("402881973aa11bb7013aa11bb8340000");foo.setName("asdas");foo.notifyUpdater("name");//只更新name字段baseDao.update(foo);}