怎样使用Hibernate中的merge方法 merge方法和update方法的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎样使用Hibernate中的merge方法 merge方法和update方法的区别相关的知识,希望对你有一定的参考价值。

使用merge方法,如果数据库中有该记录,则更新该记录,如果不存在该记录,则进行insert操作。
使用update的话,会无条件执行update,
也就是说如果数据库中有该记录的话,merge和update是一样的,但是如果数据库中没有该记录,使用merge执行insert,不会报错,而使用update会报错。
参考技术A 先去了解一下持久化类~
假设有个Student stu类,session.update(stu),merge也是一样
不同之处在于,update可以持久化类,merge不行,执行session.merge(stu)之后,stu对象如果之前不是持久化状态,stu对象依然不会被关联到session上
如果session中存在相同的持久化类,merge是会改变session中的原有持久化实例的状态的

hibernate save,update,saveorupdate方法有什么区别

save肯定是添加的时候用,update修改时候用,saveorupdate是添加或修改,如果真是这样save和update的存在就没意义了,我们直接saveorupdate就行了。
save在添加用的时候  不会出现索引机制(即遍历目录  效率最高)
update在修改时候要遍历 不存在则会异常
saveorupdate是优先遍历 如果不存在则创建(效率最低)
merge和saveOrUpdate方法区别在于:merge方法是把我们提供的对象转变为托管状态的对象;而saveOrUpdate则是把我们提供的对象变成一个持久化对象;
说的通俗一点就是:saveOrUpdate后的对象会纳入session的管理,对象的状态会跟数据库同步,再次查询该对象会直接从session中取,merge后的对 
象不会纳入session的管理,再次查询该对象还是会从数据库中取。

 Session的save()方法使一个临时对象转变为持久化对象。例如以下代码保存一个Customer对象:

        Customer customer = new Customer();

        customer.setId(new Long(9)); // 为Customer临时对象设置OID是无效的

        customer.setName("Tom");

        Session session = sessionFactory.openSession();

        Transaction tx = session.beginTransaction();

        session.save(customer);

        session.close();

    Session的save()方法完成以下的操作:

    (1)把Customer对象加入到缓存中,使它变为持久化对象。

    (2)选用映射文件指定的标识符生成器为持久化对象分配惟一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定标识符生成器:

        <id name="id" column="ID">

            <generator class="increment"/>

        </id>

    以上程序试图通过setId()方法为Customer临时对象设置OID是无效的。假如起初CUSTOMERS表中没有记录,那么执行完save()方法后,Customer对象的ID为1。如果希望由程序来为Customer对象指定OID,可以调用save()的另一个重载方法:

        save(customer, new Long(9));

    以上save()方法的第二参数显示指定Customer对象的OID。这种形式的save()方法不推荐使用,尤其在使用代理主键的场合,不应该由程序为持久化对象指定OID。

    (3)计划执行一个insert语句,把Customer对象当前的属性值组装到insert语句中:

        insert into CUSTOMERS(ID, NAME, ......) values(1, ‘Tom‘, ......);

    值得注意的是,save()方法并不立即执行SQL insert语句。只有当Session清理缓存时,才会执行SQL insert语句。如果在save()方法之后,又修改了持久化对象的属性,这会使得Session在清理缓存时,额外执行SQL update语句。以下两段代码尽管都能完成相同的功能,但是左边代码仅执行一条SQL insert语句,而右边代码执行一条SQL insert和一条SQL update语句。左边代码减少了操作数据库的次数,具有更好的运行性能。

        Customer customer = new Customer();         Customer customer = new Customer();

        // 先设置Customer对象的属性,再保存它       session.save(customer);

        customer.setName("Tom");                    // 先保存Customer对象,再修改它的属性

        session.save(customer);                    customer.setName("Tom");

        transaction.commit();                      transaction.commit();

    Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当Customer对象处于持久化状态时,不允许程序随意修改它的OID,例如:

        Customer customer = new Customer();

        session.save(customer);

        customer.setId(new Long(100)); // 抛出HibernateException

        transaction.commit();

    以上代码会导致Session在清理缓存时抛出异常。

 


技术分享无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的OID。因此,比较安全的做法是,在定义持久化类时,把它的setId()方法高为private类型,禁止外部程序访问该方法。


 

    Session的save()方法是用来持久化一个临时对象的。在应用程序中不应该把持久化对象或游离对象传给save()方法。例如以下代码两次调用了Session的save()方法,第二次传给save()方法的Customer对角处于持久化状态,这步操作其它是多余的:

        Customer customer = new Customer();

        session.save(customer);

        customer.setName("Tom");

        session.save(customer); // 这步操作是多余的

       transaction.commit();

    在例如以下代码把Customer游离对象传给session2的save()方法,session2会把它当做临时对象处理,再次向数据库中插入一条Customer记录:

        Customer customer = new Customer();

        customer.setName("Tom");

        Session session1 = sessionFactory.openSession();

        Transaction tx1 = session1.beginTransaction();

        session1.save(customer); // 此时Customer对象的ID变为1

        tx1.commit();

        session1.close(); // 此时Customer对象变为游离对象

        Session session2 = sessionFactory.openSession();

        Transaction tx2 = session2.beginTransaction();

        session2.save(cutomer); // 此时Customer对象的ID变为2

        tx2.commit();

        session2.close();

    尽管以上程序代码能正常运行,但是会导致CUSTOMERS表中有两条代表相同业务实体的记录,因此不符合业务逻辑。

 

hibernate提供了saveOrUpdate的方法来进行数据库的操作。hibernate会根据对象的状态决定是insert还是update,
其根本是通过xml文件中unsaved-value来确定的。如果设置null,系统会根据传入的对象的id的值判断,如果是null,
则表示对象不存在,那么insert;如果不是Null,则表示已经存在,那么update.如果设置为none,那么表示对象不存在,
会始终调用insert;如果设置为any,那么表示对象始终存在,会始终调用update



    数据库表主键的知识点:
    Generator 为每个 POJO 的实例提供唯一标识。一般情况,我们使用“native”。
class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 实现的某个实例,其中包括:


   “assigned” 主键由外部程序负责生成,在 save() 之前指定一个。


   “hilo” 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。


   “seqhilo”与hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,
            适用于支持 Sequence 的数据库,如Oracle。


   “increment”主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,
            之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。


   “identity”采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL 中的主键生成机制。


   “sequence”采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。


   “native”由 Hibernate 根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式。


   “uuid.hex”由 Hibernate 基于128 位 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。


   “uuid.string”与uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中。


   “foreign”使用另外一个相关联的对象的标识符作为主键。


   以下举例:
   1、指定参数的情况:
      <id name="id" unsaved-value="0"> 
        <generator class="sequence">
          <param name="sequence">SEQ_CHILD</param>
        </generator>
      </id>
     使用的是sequence,适合oracle数据库;


   2、对于sql server2000中的数据库子增字段,在配置文件使用下列方法实现:
      <id name="id" type="long" unsaved-value="0">
        <column name="id" sql-type="numeric" not-null="true" />
        <generator class="identity" /> 
      </id>
    这里主要是:identity:代表由sql server2000数据库自己提供子增字段.如果要hibernate自己提供,则用increment关键字来实现


   3、如果表中的主键用字符串类型:可以用hibernate自己提供的方法实现主键唯一:
     <id name="id" type="string" unsaved-value="null">
         <column name="cid" sql-type="char(32)" not-null="true" />
         <generator class="uuid.hex" />
     </id>
     使用的是uuid.hex: 采用128位的算法来生成一个32位字符串。最通用的一种方式。适用于所有数据库。


   重要的知识点:
   1. 如果有部门表,有员工表,员工表中有dep_id,则表部门类和员工类是one-to-many的关系:
      可以使用: ( 在部门类department中使用下列)
      Department类: 
        /** 部门的所有员工   */
       private Set staffs = new TreeSet();
     
       xml的文件:
         <set name="staffs" >
             <key column="dep_id"/>
             <one-to-many class="hbp.sys.data.Staff"/>
         </set>
         如果是list,需要用索引<index> </index>,具体其中的含义,不是很明白.待以后研究
      
    2. 如果部门要有一个负责人,即部门表(tb_department)中有一个字段:staff_id.
        那么表示部门和负责人之间的关系是many-to-one的关系
        Department类: 
         /** 部门负责人id */
       private Staff staff;
    
       xml 文件
        <many-to-one name="staff" class="hbp.sys.data.Staff" column="staff_id"/> 

    3. 多对多关系,一般我们是做一个中间关联表.我用角色和权限做了个例子,
         Right(id,name)     Role(id,name)   中间表:tb_role_right(role_id,right_id)
         Right类中有一个Role的集合:private Set roles=new TreeSet();
         Role类中也有一个Right的集合:private Set rights=new TreeSet();
         则两者是明显的多对多关系.使用many-to-many来实现;
         xml文件中
         right.hbm.xml:如下:
           <set name="roles" table="tb_role_right" cascade="all">
              <key column="right_id"/>
              <many-to-many column="role_id" class="hbp.sys.data.Role"/>
           </set> 
         role.hbm.xml文件中类似:
           <set name="rights" table="tb_role_right" cascade="all">
             <key column="role_id"/>
             <many-to-many column="right_id" class="hbp.sys.data.Right"/>
           </set>


   4. 几个值得注意的问题:
           a)在xml?映射文件中,写类的名字时一定用类的全名:即:包+类名如:(hbp.sys.data.Staff),这个错误使我费了半天劲.:(
           b)我在写实现基本DAO操作时,写了
                session.delete("from Right as right where right.id="+id); 
                程序死活报错,我折腾了半天,跟踪到底,才恍然大悟,hibernate在解析sql语句的时候把
                其中的right,当成了数据库中的右连接("保留字"),唉,这种关键字,不能随便用啊,:)


   5. hibernate中HQL语言的查询根据你的sql的不同而返回不同的对象类型.
            如果你使用session.find(String hql)
            一般会返回一个List,如:from Staff staff;返回的是包含所有的员工对象的集合
            如你的hql为:select count(*) from Staff staff;则返回的是一个Integer对象
            如果你使用的hql为:select count(distinct staff.name),count(*) from Staff staff;则返回的是一个Object
            即Object[],需要先把他转换成Object[],然后在取[0],[1].
            这种设计我不知道hibernate是如何处理的,感觉既好也不好.好的是可以使用一个find获得任意查询
            不好在于根据hql来处理返回结果,容易出错.

以上是关于怎样使用Hibernate中的merge方法 merge方法和update方法的区别的主要内容,如果未能解决你的问题,请参考以下文章

hibernate中的merge()方法

谁懂得hibernate的merge方法?

hibernate.merge()方法怎么用

hibernate的merge和saveOrUpdate方法有啥区别

在 Hibernate 中调用 merge() 方法

Hibernate中的merge使用详情解说