`

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 SQL
			updateStrings = getUpdateStrings(
					rowId != null,
					hasUninitializedLazyProperties( object )
			);
			propsToUpdate = getPropertyUpdateability( object );
		}

可以看到hibernate提供了3种的获得更新字段方式,默认情况下会进入最后的else中获得所有的字段,中间else if 的方式可以看到注解里的说明,应该是为read-only的对象准备,此处没有详细深入,而我们需要的是第一种方式
当对象的mapping文件配置了dynamic-update="true" 且dirtyFields存在时hibernate便会根据dirtyFields获得需要更新的字段
propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
这里的dirtyFields便是entity对应的字段下标
一直向前追溯dirtyFields的来源可以在org.hibernate.event.internal.DefaultFlushEntityEventListener中找到protected void dirtyCheck(final FlushEntityEvent event) throws HibernateException函数
关键代码:
int[] dirtyProperties = session.getInterceptor().findDirty(
				entity,
				id,
				values,
				loadedState,
				persister.getPropertyNames(),
				persister.getPropertyTypes()
		);

hibernate在session中做了一个拦截器,可以在操作前做一些处理。
session由sessionFactory创建,而interceptor则由从Configuration注入
这样就很清楚了,我们要做的操作只要在Configuration中注入我们自定义的interceptor就行了

先写一个interceptor继承与emtpyInterceptor
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 {

	@Override
	public 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;
	}
	
}


这里我做了3种更新方式,一种为基本的全字段更新,一种为根据程序员自定义的字段更新,一种为核查原始数据和新数据是否相等,更新不相等的字段。


基于spring的配置,我们的hibernate的Configuration是由LocalSessionFactoryBean创建的。
具体配置为:
<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</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>

具体的过程可以在spring的源码中找到,此处我们注入上面的dirtyFindInterceptor类。


测试:

<hibernate-mapping package="net.esj.test.pojo">
	<class
		name="Foo"
		table="test_foo" dynamic-update="true"  //dynamic-update="true"此须增加
	>
		<meta attribute="sync-DAO">true</meta>
		<id
			name="id"
			type="string"
			column="id"
		>
			<generator class="uuid"/>
		</id>

		<property
			name="name"
			column="name"
			type="string"
			not-null="false"
		/>
		<property
			name="code"
			column="code"
			type="integer"
			not-null="false"
		/>



	</class>	
</hibernate-mapping>

public void testUpdate(){
		Foo foo = new Foo();
		foo.setId("402881973aa11bb7013aa11bb8340000");
		foo.setName("asdas");
		foo.notifyUpdater("name");//只更新name字段
		baseDao.update(foo);
	}


hibernate打印的sql:
Hibernate: update test_foo set name=? where id=?
可以看到现在只更新了name字段
分享到:
评论

相关推荐

    spring mvc + spring + hibernate 全注解整合开发视频教程 11

    在本教程中,我们将深入探讨如何使用Spring MVC、Spring和Hibernate三大框架进行全注解的整合开发。这个视频教程系列的第11部分,重点可能是建立在前几部分的基础之上,进一步深化对这三个核心技术的理解和实践。 ...

    Spring+Hibernate处理Oracle lob字段(一)

    在本文中,我们将深入探讨如何在Java环境下,利用Spring和Hibernate框架处理Oracle数据库中的LOB(Large Object)字段。LOB字段通常用于存储大体积的数据,如文本、图片或视频。在实际开发中,处理这类数据时可能会...

    spring mvc + spring + hibernate 全注解整合开发视频教程 04

    在本视频教程“Spring MVC + Spring + Hibernate 全注解整合开发视频教程 04”中,我们将深入探讨Java企业级开发中的三大核心技术——Spring、Spring MVC和Hibernate的集成与应用,尤其是通过注解实现的简化配置。...

    使用Struts + Spring + Hibernate完成用户登陆笔记

    * 首先,创建一个名为person的表,用于存储用户信息,包括id、name和password等字段。 * 然后,使用Hibernate框架来实现数据持久化,例如使用Hibernate的createQuery方法来实现数据查询。 * 使用Struts框架来处理...

    spring+hibernate整合demo

    在IT行业中,Spring和Hibernate是两个非常重要的框架,它们分别在应用层管理和持久化层处理数据。Spring是一个全面的后端开发框架,提供了依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)以及大量的集成...

    spring+hibernate 解决大字段(clob)

    在Hibernate的`SessionFactory`配置中,需要指定`lobHandler` Bean来处理CLOB和BLOB字段: ```xml &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"&gt; ...

    spring+hibernate整合简单示例

    例如,使用@Entity注解标识实体类,@Table指定数据库表,@Column定义字段等。 5. **JPA支持**:Spring还支持JPA(Java Persistence API),这是Java官方的ORM规范。虽然Hibernate是JPA的一种实现,但Spring可以透明...

    JSF+Spring+Hibernate 分页显示

    5. **后端处理请求**:在Spring MVC的Controller中,接收前端分页请求,调用DAO层的方法获取指定页码的数据,并将其返回到JSF页面。 6. **前端交互**:JSF页面上的分页组件会根据用户的点击事件发送AJAX请求,...

    Struts+Spring+Hibernate快速入门

    Struts+Spring+Hibernate 整合是开发Java Web应用程序的一种常见模式,也称为SSH框架。这个框架组合提供了从前端到后端的完整解决方案,适用于构建三层架构的应用程序。以下是关于这个话题的详细说明: 1. **Struts...

    MyEclipse开发SSH2(Struts2+Spring+Hibernate)教程

    ### MyEclipse开发SSH2(Struts2+Spring+Hibernate)教程精析 #### 一、开发环境准备 在深入探讨如何使用MyEclipse构建SSH2框架(Struts2 + Spring + Hibernate)之前,首要任务是确保开发环境的正确搭建。本教程...

    网上订餐系统(struts+spring+hibernate)

    在本文中,我们将深入探讨使用Struts、Spring和Hibernate(SSH)框架开发这样一个系统的相关知识点。 **Struts框架** Struts是Apache软件基金会下的一个开源项目,主要负责处理MVC(Model-View-Controller)架构中...

    SpringMVC+Spring+HIbernate增删改查

    在SpringMVC+Spring+Hibernate的组合中,Hibernate作为数据访问层,提供了对数据库的CRUD(创建、读取、更新、删除)操作。通过Hibernate的Session接口,我们可以方便地进行持久化操作。 在项目中,`blogarticle....

    spring1.2+hibernate3对大字段的处理实例,供大家学习与交流,可直接运行

    这个压缩包文件"spring1.2+hibernate3对大字段的处理实例"提供了一个具体的案例,演示了如何在Spring 1.2和Hibernate 3框架下解决这个问题。下面我们将深入探讨这些知识点。 首先,Spring 1.2是一个轻量级的Java...

    JSF2.0+Spring+Hibernate实例代码

    1. **配置文件**:如`web.xml`(Web应用部署描述符)用于配置JSF和Spring的Servlet,以及Hibernate的配置文件(如`hibernate.cfg.xml`)用于设置数据库连接参数和实体映射。 2. **JSF页面**(`.xhtml`文件):包含...

    springMVC+spring+Hibernate框架

    Spring MVC、Spring 和 Hibernate 是Java开发中非常流行的三大框架,它们共同构成了企业级Web应用的基础架构。Spring MVC作为Spring框架的一部分,主要负责处理HTTP请求和响应,提供模型-视图-控制器(MVC)设计模式...

    spring+springmvc+hibernate

    整合过程包括配置 Spring 的上下文、声明式事务管理、设置 Hibernate 数据源和 SessionFactory,并将它们与 Spring MVC 结合,处理 Web 请求和数据持久化。 **源码分析** 在提供的资源中,"spring_user" 文件可能...

    struts+spring+hibernate经典整合入门myeclipse实例

    Struts、Spring和Hibernate是Java开发中非常经典的三大框架,它们的整合应用,通常被称为SSH框架集成。这个实例是专为初学者设计的,旨在帮助理解如何在MyEclipse环境中进行SSH的集成与应用。 首先,Struts是MVC...

    spring+hibernate操作oracle的clob字段

    Spring和Hibernate框架结合使用可以有效地进行CLOB字段的操作。以下是实现这一功能的关键步骤和注意事项: 1. **配置SessionFactory** 在Spring配置文件中,你需要创建一个`SessionFactory` bean,同时指定一个`...

    Struts+Spring+Hibernate整合练习

    Struts+Spring+Hibernate 整合是Java Web开发中常用的一种技术栈,它结合了三个强大的框架,分别负责表现层(Struts)、业务逻辑层(Spring)和持久化层(Hibernate)。这种组合提供了完整的MVC架构,使得开发过程...

    struts2+spring2.5+hibernate3.2整合完整项目,带数据库脚本

    它通过Hibernate.cfg.xml配置文件连接数据库,实体类(Entity)对应数据库表,属性对应字段,通过Session接口进行CRUD(创建、读取、更新、删除)操作。Hibernate的HQL(Hibernate Query Language)和Criteria API为...

Global site tag (gtag.js) - Google Analytics