`

关于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框架中,`merge`、`update`与`saveOrUpdate`这三个方法都是用于更新或保存实体对象到数据库中的,但它们之间存在着重要的区别,这些区别主要体现...

    hibernate 的saveOrUpdate

    `saveOrUpdate`是Hibernate提供的一种便捷方法,用于处理对象的保存或更新操作。在深入讲解`saveOrUpdate`之前,我们先理解一下Hibernate的基本概念。 在Hibernate中,实体类(Entity Class)代表数据库中的表,...

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

    ### Hibernate中的`save()`与`saveOrUpdate()`方法详解 #### 一、概述 在Java持久化框架Hibernate中,`save()`与`saveOrUpdate()`是非常重要的两个方法,它们用于处理对象的持久化操作。理解这两个方法的工作原理...

    关于HIBERNATE矿建

    《关于Hibernate矿建:深入理解Hibernate框架》 Hibernate,作为Java世界中一款强大的对象关系映射(ORM)框架,极大地简化了数据库操作,使得开发者能够更专注于业务逻辑,而非底层的SQL语句。本篇文章将深入探讨...

    05_传智播客hibernate教程_实体对象的三种状态与saveOrUpdate方法

    `saveOrUpdate`是Hibernate提供的一个便捷方法,它的主要作用是根据对象当前的状态来决定执行`save()`还是`update()`操作。具体来说: - 如果对象是瞬时状态,`saveOrUpdate`会调用`save()`,为对象生成一个新的...

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

    在Java的持久化框架Hibernate中,管理对象与数据库之间的交互是通过一系列的方法完成的,其中包括`save()`, `saveOrUpdate()`, `persist()`, `merge()`, 和 `update()`。这些方法各有其特点和适用场景,理解它们的...

    hibernate第一个hibernate

    - 更新(Update):通过`Session.update()`或`Session.saveOrUpdate()`更新已存在的对象。 - 删除(Delete):调用`Session.delete()`移除数据库中的记录。 6. HQL(Hibernate Query Language): Hibernate提供...

    Hibernate实战

    这涉及到使用Session的save()、saveOrUpdate()、load()、get()、update()、merge()和delete()方法。同时,还会讨论事务管理和并发控制,确保数据的一致性和完整性。 查询语言在Hibernate中主要有两种:HQL...

    Hibernate入门文档

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

    hibernate的各种保存方式的区别

    脱管状态的对象可以通过调用`save()`、`persist()`或者`saveOrUpdate()`方法重新变为持久化状态。 #### 二、save 和 update 区别 - **save** 方法主要用于将一个瞬态对象保存到数据库中。当调用此方法时,...

    Hibernate执行持久层访问步骤

    1. 添加Hibernate依赖:在项目中引入Hibernate的JAR包或者Maven/Gradle依赖。 2. 配置Hibernate:创建hibernate.cfg.xml文件,设置数据库连接参数,如驱动类名、URL、用户名、密码等。 二、实体类定义 1. 创建实体...

    Hibernate3框架系列 [ 1 ]

    在这个部分,我们将专注于Hibernate3的基本配置以及如何使用它来执行创建(Create)、读取(Read)、更新(Update)和删除(Delete)数据库操作,即CURD操作。配置 Hibernate 包括设置实体映射文件、配置文件...

    hibernate课程笔记.doc

    以下是关于Hibernate的一些关键知识点: ### 1. JDBC 的优缺点 **JDBC的优点**: - 直接与数据库交互,提供了简单且跨平台的接口。 - 具有高度灵活性,允许编写复杂的SQL语句。 **JDBC的缺点**: - 面向过程,无法...

    hibernate 重点知识原创

    例如,使用 `save()` 或 `saveOrUpdate()` 方法保存对象,`get()` 或 `load()` 方法获取对象,`update()` 更新对象,以及 `delete()` 删除对象。同时,Session 提供了事务管理能力,可以确保数据操作的原子性和一致...

    hibernate3.3 java project

    对于那些初次接触Hibernate框架或者正在学习Java持久化技术的人来说,这是一个非常实用的学习资源。通过这个项目,初学者能够了解并掌握如何在实际应用中运用Hibernate进行数据库交互。 **一、Hibernate概述** ...

    hibernate 课件 hibernate 课件

    在这个“hibernate 课件”中,我们可以期待学习到关于Hibernate的核心概念、配置、实体管理、数据持久化、查询语言(HQL)以及事务管理等多方面的内容。 首先,我们要理解Hibernate的基本原理。Hibernate通过映射...

    Hibernate之API初识及增删改查实现案例hibernate003

    - **更新(Update)**:对持久化对象的属性进行修改后,调用`Session.update()`或`Session.saveOrUpdate()`来更新数据库。 - **删除(Delete)**:`Session.delete()`方法用于从数据库中移除对象。 7. **事务管理...

    MLDN_Hibernate开发实战讲解视频教程

    【标题】"MLDN_Hibernate开发实战讲解视频教程"涵盖了关于Hibernate框架的深入学习和实践应用,这是一款广泛用于Java企业级应用中的对象关系映射(ORM)工具。Hibernate简化了数据库操作,允许开发者用面向对象的...

    Hibernate持久层方法汇总

    本文将详细介绍Hibernate持久层中的一些核心方法,包括`session.load`, `session.find`, `session.iterator`, `session.save`, `session.update`, `session.saveorupdate`。 1. `session.load()`: - `load()`方法...

Global site tag (gtag.js) - Google Analytics