`

关于Hibernate的update或者saveOrUpdate

 
阅读更多

查看文章
   
Hibernate save and saveOrUpdate
2009-11-18 13:40

org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: 
Batch update returned unexpected row count from update [0]; 
actual row count: 0; expected: 1; 
nested exception is org.hibernate.StaleStateException: 
Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

在com.shs123.model.Contact.hbm.xml中,

原来:<id name="contactID" type="java.lang.Integer" unsaved-value="null"> //unsaved-value="null"默认值,可以不写
   <column name="contact_ID" not-null="true"/>
   <generator class="identity"/>
</id>

更改后:<id name="contactID" type="java.lang.Integer" unsaved-value="0">
   <column name="contact_ID" not-null="true"/>
   <generator class="identity"/>
</id>

debug跟踪:在insert数据的时候,contactID的值为0, 而我原来设置的是unsaved-value="null"

所以当我用saveOrUpdate,在Insert的时候,就不能写数据到数据库

 

 

 

 

---------------------------------------------

一、saveorUpdate与unsaved-value 
到底是sava还是update 
Hibernate需要判断被操作的对象究竟是一个已经持久化过的持久对象还是临时对象。 
1).主键Hibernate的id generator产生 
<id name="id" type="java.lang.Long"> 
     <column name="ID" precision="22" scale="0" /> 
      <generator class="increment" /> 
</id>

Project project = new Project(); 
project.setId(XXX); 
this.projectDao.saveOrUpdate(project);

1、默认unsaved-value="null" 
主键是对象类型,hebernate判断project的主键是否位null,来判断project是否已被持久化 
是的话,对project对象发送save(project), 
若自己设置了主键则直接生成update的sql,发送update(project),即便数据库里没有那条记录。 
主键是基本类型如int/long/double/ 
自己设置unsaved-null="0"。 
所以这样的话save和update操作肯定不会报错。

2、unsaved-value="none", 
由于不论主键属性为任何值,都不可能为none,因此Hibernate总是对project对象发送update(project)

3、unsaved-value="any" 
由于不论主键属性为任何值,都肯定为any,因此Hibernate总是对project对象发送save(project),hibernate生成主键。

Hibernate文档中写到 
saveOrUpdate()完成了如下工作: 
如果对象已经在这个session中持久化过了,什么都不用做 
如果对象没有标识值,调用save()来保存它 
如果对象的标识值与unsaved-value中的条件匹配,调用save()来保存它 
如果对象使用了版本(version或timestamp),那么除非设置unsaved-value="undefined",版本检查会发生在标识符检查之前. 
如果这个session中有另外一个对象具有同样的标识符,抛出一个异常

2).主键由自己来赋值 
<id name="id" type="java.lang.Long"> 
       <column name="ID" precision="22" scale="0" /> 
       <generator class="assigned" /> 
</id>

Project project = new Project(); 
project.setId(XXX); 
this.projectDao.saveOrUpdate(project);

1、默认unsaved-value="null" 
这时有所不同,hibernate会根据主键产生一个select,来判断此对象是否已被持久化 
已被持久化则update,未被持久化则save。 
2、unsaved-value="none",update对象,同上

3、unsaved-value="any" ,save对象, 
如果自己自己设置的ID在数据库中已存在,则报错。

二、save与update操作 
显式的使用session.save()或者session.update()操作一个对象的时候,实际上是用不到unsaved-value的 
在同一Session,save没什么可说得 
update对象时, 最直接的更改一个对象的方法就是load()它,保持Session打开,然后直接修改即可: 
Session s =… 
Project p = (Project) sess.load(Project.class, id) ); 
p.setName(“test”); 
s.flush(); 
不用调用s.update(p);hibernate能察觉到它的变化,会自动更新。当然显示调用的话也不会错

Hibernate文档中写到 
update()方法在下列情形下使用: 
程序在前面的session中装载了对象 
对象被传递到UI(界面)层 
对该对象进行了一些修改 
对象被传递回业务层 
应用程序在第二个session中调用update()保存修改

 

三、delete操作 
删除时直接自己构造一个project即可删除 
this.projectDao.delete(preojct);

以前删除我是这样写的 
public void deleteProject(String id) { 
Project project = (Project) this.projectDao.get(Project.class, id); 
if (project != null) { 
this.projectDao.delete(project); 

即这样也是可以的 
Project project = new Project(); 
project.setId(id); 
this.projectDao.delete(project).

如果有级联关系,需要把级联的子类也构造出来add进去,同样可以删除。

   另外今天在 javaEye网站上发现一篇很好的帖子:

   ——————————————————《saveOrupdate方法如何使用 》——————————————————

假设你的PO不需要跨Session的话,那么就不需要用到,例如你打开一个Session,对PO进行操作,然后关闭,之后这个PO你也不会再用到了,那么就不需要用update。

因此,我们来看看: 
Java代码 
Foo foo=sess.load(Foo.class,id);;    
foo.setXXX(xxx);;    
sess.flush();;   
sess.commit();;  

 

PO对象foo的操作都在一个Session生命周期内完成,因此不需要显式的进行sess.update(foo)这样的操作。 Hibernate会自动监测到foo对象已经被修改过,因此就向数据库发送一个update的sql。当然如果你非要加上 sess.update(foo)也不会错,只不过这样做没有任何必要。

而跨Session的意思就是说这个PO对象在Session关闭之后,你还把它当做一个VO来用,后来你在Session外面又修改了它的属性,然后你又想打开一个Session,把VO的属性修改保存到数据库里面,那么你就需要用update了。

Java代码 
// in the first session    
Cat cat = (Cat); firstSession.load(Cat.class, catId);;    
Cat potentialMate = new Cat();;    
firstSession.save(potentialMate);;    

// in a higher tier of the application    
cat.setMate(potentialMate);;    

// later, in a new session    
secondSession.update(cat);; // update cat    
secondSession.update(mate);; // update mate

cat和mate对象是在第一个session中取得的,在第一个session关闭之后,他们就成了PO的第三种状态,和Session已经 detached的PO,此时他们的状态信息仍然被保留下来了。当他们进入第二个session之后,立刻就可以进行状态的更新。但是由于对cat的修改 操作:cat.setMate(potentialMate); 是在Session外面进行的,Hibernate不可能知道cat对象已经被改过了,第二个Session并不知道这种修改,因此一定要显式的调用 secondSession.update(cat); 通知Hibernate,cat对象已经修改了,你必须发送update的sql了。

所以update的作用就在于此,它只会被用于当一个PO对象跨Session进行状态同步的时候才需要写。而一个PO对象当它不需要跨Session进行状态管理的时候,是不需要写update的。

再谈谈saveOrUpdate的用场:

saveOrUpdate和update的区别就在于在跨Session的PO状态管理中,Hibernate对PO采取何种策略。

例如当你写一个DAOImpl的时候,让cat对象增加一个mate,如下定义: 
Java代码 
public void addMate(Cat cat, Mate mate); {   
    Session session = ...;   
    Transacton tx = ...;   
    session.update(cat);;   
    cat.addMate(mate);;   
    tx.commit();;   
    session.close();;   
};

 

显然你是需要把Hibernate的操作封装在DAO里面的,让业务层的程序员和Web层的程序员不需要了解Hibernate,直接对DAO进行调用。

此时问题就来了:上面的代码运行正确有一个必要的前提,那就是方法调用参数cat对象必须是一个已经被持久化过的PO,也就是来说,它应该首先从数 据库查询出来,然后才能这样用。但是业务层的程序员显然不知道这种内部的玄妙,如果他的业务是现在增加一个cat,然后再增加它的mate,他显然会这样 调用,new一个cat对象出来,然后就addMate:

Java代码 
Cat cat = new Cat();;   
cat.setXXX();;   
daoimpl.addMate(cat,mate);;

 

但是请注意看,这个cat对象只是一个VO,它没有被持久化过,它还不是PO,它没有资格调用addMate方法,因此调用addMate方法不会 真正往数据库里面发送update的sql,这个cat对象必须先被save到数据库,在真正成为一个PO之后,才具备addMate的资格。

你必须这样来操作:

Java代码 
Cat cat = new Cat();;   
cat.setXXX();;   
daoimpl.addCat(cat);;   
daoimpl.addMate(cat, mate);;

 

先持久化cat,然后才能对cat进行其他的持久化操作。因此要求业务层的程序员必须清楚cat对象处于何种状态,到底是第一种,还是第三种。如果是第一种,就要先save,再addMate;如果是第三种,就直接addMate。

但是最致命的是,如果整个软件分层很多,业务层的程序员他拿到这个cat对象也可能是上层Web应用层传递过来的cat,他自己也不知道这个cat究竟是VO,没有被持久化过,还是已经被持久化过,那么他根本就没有办法写程序了。

所以这样的DAOImpl显然是有问题的,它会对业务层的程序员造成很多编程上的陷阱,业务层的程序员必须深刻的了解他调用的每个DAO对PO对象 进行了何种状态管理,必须深刻的了解他的PO对象在任何时候处于什么确切的状态,才能保证编程的正确性,显然这是做不到的,但是有了 saveOrUpdate,这些问题就迎刃而解了。

现在你需要修改addMate方法:

Java代码 
public void addMate(Cat cat, Mate mate); {   
    Session session = ...;   
    Transacton tx = ...;   
    session.saveOrUpdate(cat);;   
    cat.addMate(mate);;   
    tx.commit();;   
    session.close();;   
};

 

如上,如果业务层的程序员传进来的是一个已经持久化过的PO对象,那么Hibernate会更新cat对象(假设业务层的程序员在Session外面修改过cat的属性),如果传进来的是一个新new出来的对象,那么向数据库save这个PO对象。

BTW: Hibernate此时究竟采取更新cat对象,还是save cat对象,取决于unsave-value的设定。

这样,业务层的程序员就不必再操心PO的状态问题了,对于他们来说,不管cat是new出来的对象,只是一个VO也好;还是从数据库查询出来的的PO对象也好,全部都是直接addMate就OK了:

Java代码 
daoimple.addMate(cat, mate);;

daoimple.addMate(cat, mate);;

这便是saveOrUpdate的作用。

————————————————-精华帖子————————————————————
当你显式的使用session.save()或者session.update()操作一个对象的时候,实际上是用不到unsaved-value的。某 些情况下(父子表关联保存),当你在程序中并没有显式的使用save或者update一个持久对象,那么Hibernate需要判断被操作的对象究竟是一 个已经持久化过的持久对象,是一个尚未被持久化过的内存临时对象。例如:

Java代码 
Session session = ...;   
Transaction tx = ...;   

Parent parent = (Parent); session.load(Parent.class, id);;   

Child child = new Child();;   
child.setParent(parent);;   
child.setName("sun");;   

parent.addChild(child);;   
s.update(parent);;   

s.flush();;   
tx.commit();;   
s.close();;

在上例中,程序并没有显式的session.save(child); 那么Hibernate需要知道child究竟是一个临时对象,还是已经在数据库中有的持久对象。如果child是一个新创建的临时对象(本例中就是这种 情况),那么Hibernate应该自动产生session.save(child)这样的操作,如果child是已经在数据库中有的持久对象,那么 Hibernate应该自动产生session.update(child)这样的操作。

因此我们需要暗示一下Hibernate,究竟child对象应该对它自动save还是update。在上例中,显然我们应该暗示 Hibernate对child自动save,而不是自动update。那么Hibernate如何判断究竟对child是save还是update呢? 它会取一下child的主键属性 child.getId() ,这里假设id是 java.lang.Integer类型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等,那么Hibernate认为 child是新的内存临时对象,发送save,如果不相等,那么Hibernate认为child是已经持久过的对象,发送update。

unsaved-value="null" (默认情况,适用于大多数对象类型主键 Integer/Long/String/...)

当Hibernate取一下child的Id,取出来的是null(在上例中肯定取出来的是null),和unsaved-value设定值相等,发送save(child)

当Hibernate取一下child的id,取出来的不是null,那么和unsaved-value设定值不相等,发送update(child)

例如下面的情况:

Java代码 
Session session = ...;   
Transaction tx = ...;   

Parent parent = (Parent); session.load(Parent.class, id);;   
Child child = (Child); session.load(Child.class, childId);;   

child.setParent(parent);;   
child.setName("sun");;   

parent.addChild(child);;   
s.update(parent);;   

s.flush();;   
tx.commit();;   
s.close();;

child已经在数据库中有了,是一个持久化的对象,不是新创建的,因此我们希望Hibernate发送update(child),在该例 中,Hibernate取一下child.getId(),和unsave-value指定的null比对一下,发现不相等,那么发送 update(child)。

BTW: parent对象不需要操心,因为程序显式的对parent有load操作和update的操作,不需要Hibernate自己来判断究竟是save还是 update了。我们要注意的只是child对象的操作。另外unsaved-value是定义在Child类的主键属性中的。

Java代码 
<class name="Child" table="child">   
<id column="id" name="id" type="integer" unsaved-value="null">   
<generator class="identity"/>   
</id>   
...   
</class>

 

如果主键属性不是对象型,而是基本类型,如int/long/double/...,那么你需要指定一个数值型的unsaved-value,例如:

Java代码 
unsaved-null="0"

unsaved-null="0"

在此提醒大家,很多人以为对主键属性定义为int/long,比定义为Integer/Long运行效率来得高,认为基本类型不需要进行对象的封装 和解构操作,因此喜欢把主键定义为int/long的。但实际上,Hibernate内部总是把主键转换为对象型进行操作的,就算你定义为int /long型的,Hibernate内部也要进行一次对象构造操作,返回给你的时候,还要进行解构操作,效率可能反而低也说不定。因此大家一定要扭转一个 观点,在Hibernate中,主键属性定义为基本类型,并不能够比定义为对象型效率来的高,而且也多了很多麻烦,因此建议大家使用对象型的 Integer/Long定义主键。

unsaved-value="none"和 
unsaved-value="any"

主主要用在主键属性不是通过Hibernate生成,而是程序自己setId()的时候。

在这里多说一句,强烈建议使用Hibernate的id generator,或者你可以自己扩展Hibernate的id generator,特别注意不要使用有实际含义的字段当做主键来用!例如用户类User,很多人喜欢用用户登陆名称做为主键,这是一个很不好的习惯,当 用户类和其他实体类有关联关系的时候,万一你需要修改用户登陆名称,一改就需要改好几张表中的数据。偶合性太高,而如果你使用无业务意义的id generator,那么修改用户名称,就只修改user表就行了。

由这个问题引申出来,如果你严格按照这个原则来设计数据库,那么你基本上是用不到手工来setId()的,你用Hibernate的id generator就OK了。因此你也不需要了解当

unsaved-value="none"和 
unsaved-value="any"

究竟有什么含义了。如果你非要用assigned不可,那么继续解释一下:

unsaved-value="none" 的时候,由于不论主键属性为任何值,都不可能为none,因此Hibernate总是对child对象发送update(child)

unsaved-value="any" 的时候,由于不论主键属性为任何值,都肯定为any,因此Hibernate总是对child对象发送save(child)

大多数情况下,你可以避免使用assigned,只有当你使用复合主键的时候不得不手工setId(),这时候需要你自己考虑究竟怎么设置unsaved-value了,根据你自己的需要来定。

BTW: Gavin King强烈不建议使用composite-id,强烈建议使用UserType。

因此,如果你在系统设计的时候,遵循如下原则:

1、使用Hibernate的id generator来生成无业务意义的主键,不使用有业务含义的字段做主键,不使用assigned。

2、使用对象类型(String/Integer/Long/...)来做主键,而不使用基础类型(int/long/...)做主键

3、不使用composite-id来处理复合主键的情况,而使用UserType来处理该种情况。

那么你永远用的是unsaved-value="null" ,不可能用到any/none/..了。

分享到:
评论

相关推荐

    Hibernate merge、update与saveOrUpdate方法的区别

    Hibernate merge、update与saveOrUpdate方法的区别

    hibernate 的saveOrUpdate

    saveOrUpdate

    Hibernate入门文档

    Hibernate 难点解析: hibernate中uuid和native的相关总结 Hibernate get与load的区别 hibernate二级缓存中list和iterator的用法 Hibernate中的对象有三种状态: lock和update区别 save 和...update 和saveOrUpdate区别

    Hibernate的Session的javadoc

    我身边的朋友经常会分不清save、saveOrUpdate、update的区别,lock、merge、replicate、refresh、evict甚至不知道是干什么用的。而且关于实体对象的生命周期也有很多概念不清,分不清transient、persistent、...

    Hibernate持久层方法汇总

    Hibernate持久层方法汇总 session.load, session.find, session.iterator, session.save, session.update, session.saveorupdate

    hibernate基础 二 关联映射 级联操作

    hibernate 的基础 常用的主键生成策略 持久化对象的三种状态 一级缓存的常用操作(save/update/saveOrUpdate/delete)一对多关联映射 级联操作

    Hibernate save() saveorupdate()的用法第1/2页

    一个PO有三种状态: 1、未被持久化的VO 此时就是一个内存对象VO,由JVM管理生命周期 2、已被持久化的PO,并且在Session生命周期内 此时映射数据库数据,由数据库管理生命周期 3、曾被持久化过,但现在和Session已经...

    hibernate操作数据库笔记

    (2).saveOrUpdate(Object obj); (3).delete(Object obj); //只要删除的对象设置了主键的值就可以删除而无需先获取 (4).update(Object obj); //只要更新的对象设置了主键的值就可以更新而无需先获取 (5).get...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     8.4.4 Session的saveOrUpdate()方法  8.4.5 Session的merge()方法  8.4.6 Session的delete()方法  8.4.7 Session的replicate()方法  8.5 级联操纵对象图  8.5.1 级联保存临时对象  8.5.2 更新持久化对象  ...

    save, saveOrUpdate, persist, merge, update 区别.docx

    hibernate save()方法能够保存实体到数据库,正如方法名称save这个单词所表明的意思。我们能够在事务之外调用这个方法,这也是我不喜欢使用这个方法保存数据的原因。 假如两个实体之间有关系(例如employee表和...

    精通hibernate:对象持久化技术孙卫琴第二版part2

    8.4.4 Session的saveOrUpdate()方法 197 8.4.5 Session的merge()方法 198 8.4.6 Session的delete()方法 200 8.4.7 Session的replicate()方法 201 8.5 级联操纵对象图 202 8.5.1 级联保存临时对象 206 8.5.2 ...

    第24次课-1 Spring与Hibernate的整合

    void saveOrUpdate(Object entity):根据实例状态,选择保存或更新 void update(Object entity):更新实例的状态 void setMaxResults(int maxResults):设置分页的大小 24.3 Spring对Hibernate的简化 24.3.4 ...

    基于泛型的通用Dao接口和hibernate的实现

    // HibernateUtil.getSession().saveOrUpdate(entity); entity = (T) HibernateUtil.getSession().merge(entity); } @SuppressWarnings("unchecked") public T findById(Serializable id) { return (T) ...

    精通Hibernate:对象持久化技术第二版part3

    8.4.4 Session的saveOrUpdate()方法 197 8.4.5 Session的merge()方法 198 8.4.6 Session的delete()方法 200 8.4.7 Session的replicate()方法 201 8.5 级联操纵对象图 202 8.5.1 级联保存临时对象 206 8.5.2 ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

     8.4.4 Session的saveOrUpdate()方法  8.4.5 Session的merge()方法  8.4.6 Session的delete()方法  8.4.7 Session的replicate()方法  8.5 级联操纵对象图  8.5.1 级联保存临时对象  8.5.2 更新持久化对象  ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

     8.4.4 Session的saveOrUpdate()方法  8.4.5 Session的merge()方法  8.4.6 Session的delete()方法  8.4.7 Session的replicate()方法  8.5 级联操纵对象图  8.5.1 级联保存临时对象  8.5.2 更新持久化对象  ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

     8.4.4 Session的saveOrUpdate()方法  8.4.5 Session的merge()方法  8.4.6 Session的delete()方法  8.4.7 Session的replicate()方法  8.5 级联操纵对象图  8.5.1 级联保存临时对象  8.5.2 更新持久化对象  ...

    Java求职面试宝典各大公司常考知识点

    1.7. update()和saveOrUpdate()的区别? 4 1.8. hibernate拒绝连接、服务器崩溃的原因?最少写5个 4 1.9. hibernate如何管理缓存 4 1.10. 使用Hibernate的优点: 6 1.11. Hibernate是如何延迟加载? 6 1.12. ...

    oracle 数据按主键删除慢问题的解决方法

    所以将原先使用hibernate的saveOrupdate方法,改查jdbc的 sql语句来处理update和delete数据操作。但是依然没效果!!! 怀疑数据库出问题了!~  于是拿sql语句在PL/SQL客户端执行,查看执行计划。删除和更新都能...

Global site tag (gtag.js) - Google Analytics