Hibernate操作和保存方式

Session API

【Java Hibernate 之 CRUD 操作】http://www.codeceo.com/article/java-hibernate-crud.html
 
【Session的入门常用方法】
Query query = session.createQuery(hql):利用hql查询语句查询;
Criteria critera = session.createCriteria(Class clazz);
Transaction tx = session.beginTransaction();     //开始事务;tx.commit()提交事务;
session.close();//关闭Session,此后被session管理的持久化对象变为脱管状态;
session.save(Object obj);    //添加
session.update(Object obj);     //更新
session.delete(Object obj);    //删除
Object obj = session.get(Class clazz,Serialiazble id);    //根据主键查找记录并返回;
Object obj = session.load(Class clazz,Serializable id);    //和get方法效果一样,但是是懒加载,即在不使用他之前他不会返回对象;
 
 

游离对象

【临时状态transient、持久状态persistent和游离状态detached 】
 
【游离对象】是由持久化对象转变过来的,因此可能在数据库中还存在对应的记录 。从持久化的数据转化而来。比如session关闭或者从session里delete这个对象。
        原来同Session有关联关系,但当下却没有关联关系了,这样的对象就是detached的对象。
        session的update()或者saveOrUpdate()方法,重新将该detached对象同相应的seesion建立关联关系。
 
【临时对象】在数据库中没有对应的记录。 和hibernate session无关。事物提交也不能改变数据库对应的数据

        Transient对象指的是新建的持久化类的实例,它还从未同Hibernate的任何Session有过关联关系。

        persist()或者save()方法,将transient对象变成persistent对象。

(1)当通过get或load方法得到的po对象它们都处于persistent,但如果执行delete(po)时(但还没执行事务),该 po状态就处于detached, (表示和session脱离关联),因delete而变成游离态可以通过save或saveOrUpdate()变成持久态
(2)当一个session执行close()或clear()、evict()之后,session缓存中的persistent的po对象也变成detached,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。

update()

 

saveorupdate()如果传入的对象在数据库中有就做update操作,如果没有就做save操作。

save()在数据库中生成一条记录,如果数据库中有,会报错说有重复的记录。

update()就是更新数据库中的记录 主键在saveorupdate()方法中是起着关键作用的,只有这个主键的值不为空的时候才进行insert还是update的判断,否则直接insert若主键不为空,就可以进行saveorupdate()操作了。 saveOrUpdate是当你一个pojo对象在不确定的情况下使用的,目的在于当这个对象存在的时候就将其状态改变成现在这个状态,如果不存在就使其持久化保存现在这个状态,也就是说不论怎样就是要有这个对象 save是返回插入数据的主见的,而saveOrUpdate是void

save方法更适用于确定了是要插入,而且需要得到插入数据的主键

而saveOrUpdate更倾向于不缺定是插入还是更新,而且你不需要得到他的主键

另一方面,如果你无法确定你要插入或更新的对象是不是持久态或游离态时。如果你save一个持久态或更新一个游离态,这都是有问题的,此时你就要用到saveOrUpdate

总体来说,如果你能确定你即将操作对象的状态,则不需要用saveOrUpdate

【update()和flush区别?】

Hibernate update操作的是在自由态或脱管状态(因session的关闭而处于脱管状态)的对象,而flush是操作的在持久状态的对象。
默认情况下,一个持久状态的对象的改动(包含set容器)是不需要update的,只要你更改了对象的值,等待Hibernate flush就自动更新或保存到数据库了。
(1) 调用某些查询的和手动flush(),session的关闭、SessionFactory关闭结合。get()一个对象,把对象的属性进行改变,把资源关闭。
(2)transaction commit的时候(包含了flush) 。

 

【update()和saveOrUpdate()的区别?】

(1)update() 更新,没有主键会报错的,saveOrUpdate() 保存或更新, 没有主键就执行插入。
(2)Update:是对暂态(transient )或是只是脱管(detached)的更新操作,对于暂态对象的更新操作通常不产生效果,对于脱管对象是做了同步的操作,即数据库的数据发生变化并且对象状态也成为托管对象
SaveOrUpdate : 也是对暂态(transient )或是只是脱管(detached)的进行操作,至于是插入还是更新,则要根据(identifier)id 中指定的一些具体条件来分析,如果对象没有持久化标识(identifier)属性,对其调用save() ,否则update() 这个对象 。
(3)如果该po对象已经在本session中持久化了,在本session中执行saveOrUpdate(po)不做任何事
如果savaOrUpdate(给定id的新po)与另一个与本session关联的po对象拥有相同的持久化标识(identifier),抛出一个NonUniqueObjectException异常 :a different object with the same identifier value was already associated with the session。

 

【update()和merge()区别?】

前面说过update,基本merge和update一样。但如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象覆盖session已有的持久实例
(1)当我们使用update的时候,执行完成后,会抛出异常
(2)但当我们使用merge的时候,把处理自由态的po对象A的属性copy到session当中处于持久态的po的属性中,执行完成后原来是持久状态还是持久态,而我们提供的A还是自由态。

load()和get()的区别

Object Session.load(Class arg0, Serializable arg1) throws HibernateException

    * arg0:需要加载对象的类,例如:User.class

    * arg1:查询条件(实现了序列化接口的对象):例"4028818a245fdd0301245fdd06380001"字符串已经实现了序列化接口。

    * 此方法返回类型为Object,但返回的是代理对象。

    * 执行此方法时不会立即发出查询SQL语句。只有在使用对象时,它才发出查询SQL语句,加载对象。

    * 因为load方法实现了lazy(称为延迟加载、赖加载)

    * 延迟加载:只有真正使用这个对象的时候,才加载(才发出SQL语句)

    * hibernate延迟加载实现原理是代理方式。

    * 采用load()方法加载数据,如果数据库中没有相应的记录,则会抛出异常对象不找到(org.hibernate.ObjectNotFoundException)

Hibernate两种加载数据方式的区别:

    get()方法默认不支持lazy(延迟加载)功能,而load支持延迟加载

    get()方法在查询不到数据时,返回null,而load因为支持延迟加载,只有在使用对象时才加载,所以如果数据库中不在数据load会抛出异常(org.hibernate.ObjectNotFoundException)。

get()和load()只根据主键查询,不能根据其它字段查询,如果想根据非主键查询,可以使用HQL


如果在缓存中没有找到相应的对象,get将会直接访问数据库并返回一个完全初始化好的对象,而这个过程有可能会涉及到多个数据库调用;

而load方法在缓存中没有发现对象的情况下,只会返回一个代理对象,只有在对象getId()之外的其它方法被调用时才会真正去访问数据库,这样就能在某些情况下大幅度提高性能。


Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:

如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException。


Load方法可返回实体的代理类实例,而get方法永远直接返回实体类。


load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。

Session在加载实体对象时,将经过的过程:

首先,Hibernate中维持了两级缓存。

第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内部缓存。

而第二级缓存则存在于SessionFactory层次,由当前所有由本 SessionFactory构造的Session实例共享。

出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。
之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。 “NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。如此一来,如果 Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。

对于load方法而言,如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。


如在缓存中未发现有效数据,则发起数据库查询操作(Select SQL),如经过查询未发现对应记录,则将此次查询的信息在“NonExists”中加以记录,并返回null。
根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。
将其数据对象纳入当前Session实体管理容器(一级缓存)。
执行Interceptor.onLoad方法(如果有对应的Interceptor)。
将数据对象纳入二级缓存。
如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。
返回数据对象。

save、persist和saveOrUpdate这三个方法的不同之处?

都是用于将对象保存到数据库中的方法。

save()只能INSERT记录

saveOrUpdate()可以进行 记录的INSERT和UPDATE。

save()的返回值是一个Serializable对象

persist()方法返回值为void。

 

getCurrentSession()与openSession()

都通过Hibernate的SessionFactory获得:

1.getCurrentSession创建的session会和绑定到当前线程,而openSession不会。

2.getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭。



对于getCurrentSession()方法: 
       (1)其所在方法必须进行事务控制 
       (2)Session在第一次被使用的时候,或者第一次调用getCurrentSession()的时候,其生命周期就开始。然后它被Hibernate绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate也会把Session从当前线程剥离,并且关闭它。假若你再次调用getCurrentSession(),你会得到一个新的Session,并且开始一个新的工作单元。     
   
对于openSession()方法: 
        这个方法一般在spring与Hibernate的集成中不直接使用,它就是打开一个session,并且这个session与上下文无关,如果对其所在方法进行事务控制,会发现不起作用,原因就是前面提到的,事务控制必须确保是同一个连接,而openSession()打开的session与上下文无关。
 
这个方法与getSession(),getCurrentSession()以及getHibernateTemplate()等方法的区别在于:后面的几个方法spring可以对其进行控制。
如果对它们所在的方法进行事务控制,spring可以确保是同一个连接,而openSession()方法,spring无法对其进行控制,所以事务也不会起作用。

lock()

        Session的lock()方法重建了关联关系却并没有同数据库进行同步和更新。因此,你在使用lock()方法时一定要多加小心。顺便说一下,在进行关联关系重建时,你可以随时使用Session的update()方法同数据库进行同步。有时这个问题也可以这么来问:Session的lock()方法和update()方法之间有什么区别?。


【lock()方法与update()方法的区别】
在执行lock()方法时,会立即使用查询语句查询版本号进行版本检查,并不会执行一个update操作。 
 
而update()方法,并不会进行版本检查,直到session.flush()时候,会先进行版本检查,再进行更新操作。
 
这两个方法都把一个游离对象与一个session实例关联起来。
 
 
lock是把一个没有更改过的脱管状态的对象变成持久状态
1.调用lock把对象从脱管状态变成持久状态
2.更改持久状态的对象的内容
3.等待flush或者手动flush 
 

 
lock和update区别
update是把一个已经更改过的脱管状态的对象变成持久状态
lock是把一个没有更改过的脱管状态的对象变成持久状态(针对的是因Session的关闭而处于脱管状态的po对象(2),不能针对因delete而处于脱管状态的po对象)
对应更改一个记录的内容,两个的操作不同:
update的操作步骤是:
(1)属性改动后的脱管的对象的修改->调用update
lock的操作步骤是:
(2)调用lock把未修改的对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush

SessionFactory

SessionFactory有什么作用? SessionFactory是线程安全的吗?

    这也是Hibernate框架的常见面试问题。顾名思义,SessionFactory就是一个用于创建Hibernate的Session对象的工厂。SessionFactory通常是在应用启动时创建好的,应用程序中的代码用它来获得Session对象。作为一个单个的数据存储,它也是 线程安全的,所以多个线程可同时使用同一个SessionFactory。Java JEE应用一般只有一个SessionFactory,服务于客户请求的各线程都通过这个工厂来获得Hibernate的Session实例,这也是为什么SessionFactory接口的实现必须是线程安全的原因。还有,SessionFactory的内部状态包含着同对象关系影射有关的所有元数据,它是不可变的,一旦创建好后就不能对其进行修改了。

hibernate的各种保存方式的区别(save,persist,update,saveOrUpdte,merge,flush,lock)及对象的三种状态


hibernate的保存


hibernate对于对象的保存提供了太多的方法,他们之间有很多不同,这里细说一下,以便区别。
一、预备知识
对于hibernate,它的对象有三种状态,transient、persistent、detached
下边是常见的翻译办法:
transient:瞬态或者自由态
(new DeptPo(1,”行政部”,20,”行政相关”),该po的实例和session没有关联,该po的实例处于transient)
persistent:持久化状态
(和数据库中记录想影射的Po实例,它的状态是persistent, 通过get和load等得到的对象都是persistent)
detached:脱管状态或者游离态
(1)当通过get或load方法得到的po对象它们都处于persistent,但如果执行delete(po)时(但不能执行事务),该po状态就处于detached, (表示和session脱离关联),因delete而变成游离态可以通过save或saveOrUpdate()变成持久态
(2)当把session关闭时,session缓存中的persistent的po对象也变成detached
因关闭session而变成游离态的可以通过lock、save、update变成持久态
持久态实例可以通过调用 delete()变成脱管状态。
通过get()或load()方法得到的实例都是持久化状态的。
脱管状态的实例可以通过调用lock()或者replicate()进行持久化。

save()和persist()将会引发SQL的INSERT,delete()会引发SQLDELETE,
而update()或merge()会引发SQL UPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQL UPDATE。
saveOrUpdate()或者replicate()会引发SQLINSERT或者UPDATE


二、save 和update区别
把这一对放在第一位的原因是因为这一对是最常用的。
save的作用是把一个新的对象保存
update是把一个脱管状态的对象或自由态对象(一定要和一个记录对应)更新到数据库

三、update 和saveOrUpdate区别
这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update,而update只是update;引用hibernate reference中的一段话来解释他们的使用场合和区别
通常下面的场景会使用update()或saveOrUpdate(): 
程序在第一个session中加载对象,接着把session关闭 
该对象被传递到表现层 
对象发生了一些改动 
该对象被返回到业务逻辑层最终到持久层 
程序创建第二session调用第二个session的update()方法持久这些改动

saveOrUpdate(po)做下面的事: 
如果该po对象已经在本session中持久化了,在本session中执行saveOrUpdate不做任何事 
如果savaOrUpdate(新po)与另一个与本session关联的po对象拥有相同的持久化标识(identifier),抛出一个异常 
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.itfuture.www.po.Xtyhb#5]
saveOrUpdate如果对象没有持久化标识(identifier)属性,对其调用save() ,否则update() 这个对象

四、persist和save区别
这个是最迷离的一对,表面上看起来使用哪个都行,在hibernate reference文档中也没有明确的区分他们.
这里给出一个明确的区分。(可以跟进src看一下,虽然实现步骤类似,但是还是有细微的差别)
主要内容区别:
1,persist把一个瞬态的实例持久化,但是并"不保证"标识符(identifier主键对应的属性)被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时候。

2,save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert

五、saveOrUpdate,merge和update区别
比较update和merge
update的作用上边说了,这里说一下merge的
如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象覆盖session已有的持久实例 
(1)当我们使用update的时候,执行完成后,会抛出异常 
(2)但当我们使用merge的时候,把处理自由态的po对象A的属性copy到session当中处于持久态的po的属性中,执行完成后原来是持久状态还是持久态,而我们提供的A还是自由态

六、flush和update区别
这两个的区别好理解
update操作的是在自由态或脱管状态(因session的关闭而处于脱管状态)的对象//updateSQL
而flush是操作的在持久状态的对象。
默认情况下,一个持久状态的对象的改动(包含set容器)是不需要update的,只要你更改了对象的值,等待hibernate flush就自动更新或保存到数据库了。hibernate flush发生在以下几种情况中:
1, 调用某些查询的和手动flush(),session的关闭、SessionFactory关闭结合 
get()一个对象,把对象的属性进行改变,把资源关闭。
2,transaction commit的时候(包含了flush)


八、clear和evcit的区别
clear完整的清除session缓存
evcit(obj)把某个持久化对象从session的缓存中清空。

原文地址:https://www.cnblogs.com/lsx1993/p/4620004.html