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值相等的值,才可以进行正常的操作。