Session接口中定义的saveOrUpdate方法,集合了save和update方法,根据对象的状态来选择是进行保存还是更新,那它是怎么判断当前对象的状态的呢?API中对这个方法是这样说的,它是根据一个unsaved-value来决定的。这个值是在映射文件中的<id>标签中的一个属性。<id>标签表示的是表的主键,若主键是字符串,那么这个值默认的是空,如果主键是int型,那么这个值默认的是0。然而我们可以改变这个默认的值,虽然一般都用默认的值,但是为了弄清saveOrUpdate这个方法,我们这里对这个值进行改变,看它到底是怎么工作的。此外,为了更清楚的说明问题,对hibernate.cfg.xml文件再进行一下配置,加上这样一条语句:<property name="hibernate.show_sql">true</property>,这条语句的作用,就是将hibernate使用到的sql语句打印到控制台。

首先看下面这个例子:

User类:

public class User {
	
	private int id;
	private String name;
	private Date birthday;

	……
}



User.hbm.xml文件:

<hibernate-mapping package="com.suo.domain">
	
	<class name="User">
		<id name="id" unsaved-value="2">
			<generator class="native"/>
		</id>
		<property name="name"/>
		<property name="birthday"/>
		
	</class>
	
</hibernate-mapping>

注意:在id标签中,将unsaved-value值设置为2,默认的是0。


Test.java:

public class Test {

	public static void main(String[] args) {
		
		User user=new User();
		
		user.setId(2);//这里将id设置为2
		user.setBirthday(new Date());
		user.setName("suo");
		
		//此时的user对象是瞬时状态(Transient)
		
		System.out.println(user.getId()+" in transient status");
		
		addUser(user);
		
		//此时的user对象是脱管的(Detached)
		
		System.out.println(user.getId()+" in detached status");
	}
	
	public static void addUser(User user){
		Session session=null;
		Transaction transaction=null;
		
		try{
			session=HibernateUtil.getSession();//获得一个连接
			transaction=session.beginTransaction();//开启一个事务
			
			session.saveOrUpdate(user);
			
			System.out.println(user.getId()+" in persistent status");
			
			//此时的user对象是持久的(Persistent)
			
			transaction.commit();/*设置为自动提交,因为hibernate默认的设置为不自动提交,
								        这时候使用的数据库引擎为InnoDB,支持事务*/
			
		}catch(HibernateException e){
			if(transaction!=null){
				transaction.rollback();
			}
			e.printStackTrace();
		}finally{
			if(session!=null){
				session.close();
			}
		}
	}
}

注意:在对user进行set的时候,将id设置为2,和unsaved-value的值是相等的。


运行结果如下:

2 in transient status
Hibernate: insert into User (name, birthday) values (?, ?)
1 in persistent status
1 in detached status

根据输出的sql语句可以知道,saveOrUpdate()执行的是save方法,并且持久态的user的id值已经改变,所以save方法也执行成功了。


此时,将user的id的初始值set为3,和unsaved-value的值不相等,执行结果如下:

3 in transient status
3 in persistent status
Hibernate: update User set name=?, birthday=? where id=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

……

3 in detached status

根据输出的sql语句可以知道,saveOrUpdate执行的是update方法,但是抛出了异常,并且持久态和脱管态的对象的id没有改变,说明更新没有成功,为什么呢?

这个很明显了,saveOrUpdate方法到底是执行保存还是更新,是以对象中的id的初始值和unsaved-value值进行比较来为依据的。unsaved-value值相当于一个标志位,它定义了若这个对象没有被保存,那么它的id值应该是什么值,即处于瞬时状态的对象的id,应该具有的值。如果对象的id值和这个值相等,说明这个对象还没有被保存,即处于瞬时状态,那么saveOrUpdate方法就执行保存的操作;如果对象的id值和这个不相等,说明这个对象已经被保存过了,那么就应该执行更新的操作了。上例中,我们将user的id属性初始值设置为3,和unsaved-value的值不相等,就让hibernate误以为这个对象是保存过的了,所以它就执行了更新的操作,但是,数据库中并没有这个对象对应的记录,无法完成更新,所以就抛出了异常。

通过上面的分析,我们可以得出一个结论,即不要随便改变unsaved-value的值,尽量用它的默认值,如果真的要改变的话,就要将对象的id属性的初始值设置为和unsaved-value值相等的值,才可以进行正常的操作。