Hibernate详解(一)--session操作对象

一 session概述

  Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法.
  Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应. Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush)
站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态. 

二session缓存

在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期
Session 缓存可减少 Hibernate 应用程序访问数据库的频率。

从以下的实例来看这个缓存问题

输出结果为

我们可以看到查询语句只有一次,而且查出来的都是同一个




flush:
Session 按照缓存中对象的属性变化来同步更新数据库
默认情况下 Session 在以下时间点刷新缓存:
  • 显式调用 Session 的 flush() 方法
  • 当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后在向数据库提交事务
  • 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态
flush 缓存的例外情况: 如果对象使用 native 生成器生成 OID, 那么当调用 Session 的 save() 方法保存对象时, 会立即执行向数据库插入该实体的 insert 语句.
commit() 和 flush() 方法的区别:flush 执行一系列 sql 语句,但不提交事务;commit 方法先调用flush() 方法,然后提交事务. 意味着提交事务意味着对数据库操作永久保存下来。

若希望改变 flush 的默认时间点, 可以通过 Session 的 setFlushMode() 方法显式设定 flush 的刷新时间点 
Reflesh:

    使修改的数据库中的数据第一时间与要查询的session的数据保持一致.
比如当用户在查询过程中,数据库一些数据在这个时候修改了,要想要用户第一时间得到这些最后修改的数据,在个时候需要用到Reflesh()方法.这个时候需要用到数据库的隔离级别.
     脏读:对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
   不可重复读:对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
  幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题. 

从上到下一次编号为1,2,3,4
 <!-- 设置数据库的隔离级别 -->
         <property name="hibernate.connection.isolation">2</property>
当设置为2的时候,用户就能第一时间获取数据库修改的数据

clear()
当session调用这个方法就把session中的缓存给清除了

三.session如何转换四种状态

   hibernate把对象分为以下四种:
(1)  临时对象(Transient): 
在使用代理主键的情况下, OID 通常为 null
不处于 Session 的缓存中
在数据库中没有对应的记录
当new一个实体对象后,这个对象处于临时状态,即这个对象只是一个保存临时数据的内存
打个比方吧,工厂生产了一批货,要运往目的地,数据库是最终目的地仓库,你刚生产的商品相当于临时对象,因为它还没完成最终的使命.还没决定是否要把他放到目的地里,他还没被配送出去.他没编号.


(2)持久化对象
OID 不为 null
位于 Session 缓存中
若在数据库中已经有和其对应的记录, 持久化对象和数据库中的相关记录对应
Session 在 flush 缓存时, 会根据持久化对象的属性变化, 来同步更新数据库
在同一个 Session 实例的缓存中, 数据库表中的每条记录只对应唯一的持久化对象
这些商品经决定把他们放目的地里,他们都有自己的编号.session可以想象成使我们这些中间人,你要决定这些商品的用处.当我们把商品放在一个大仓库中存起来,这时候我们就扮演了仓库管理员的职责.

( 3)删除对象
删除对象(Removed)
在数据库中没有和其 OID 对应的记录
不再处于 Session 缓存中
一般情况下, 应用程序不该再使用被删除的对象
管理员他觉得有些商品存在问题,他就不想要这些商品,决定把他销毁

(4)游离对象(也叫”脱管”) (Detached):
OID 不为 null
不再处于 Session 缓存中
一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录
当Session进行了Close、Clear或者evict后,持久化对象虽然拥有持久化标识符和与数据
数据库对应记录一致的值,但是因为会话已经消失,对象不在持久化管理之内,所以处于游离
这个管理员的某个柜台的钥匙丢了或者管理这个对象的管理员不在了,这些柜台里的东西就无法获得了,只能找到钥匙把他转为把他转为持久化对象才能使用

下图是四种状态的转化关系,一目了然


四.session的常用操作方法

      (一)save()
  Session 的 save() 方法使一个临时对象--->持久化对象
Session 的 save() 方法完成以下操作:
把 News 对象加入到 Session 缓存中, 使它进入持久化状态
选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID. 在使用代理主键的情况下, setId() 方法为 News 对象设置 OID 使无效的.

代理主键设置可以通过*.hbm.xml文件设置
<generator class=" "/>

计划执行一条 insert 语句:在 flush 缓存的时候
Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系. 当 News 对象处于持久化状态时, 不允许程序随意修改它的 ID
persist() 和 save() 区别:
当对一个 OID 不为 Null的对象执行 save() 方法时, 会把该对象以一个新的 oid 保存到数据库中;  但执行 persist() 方法时会抛出一个异常.
 如果为null,则两个都是可以行的

(二)get() 和 load()
  都可以根据跟定的 OID 从数据库中加载一个持久化对象
区别:
  • 当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null
  • 两者采用不同的延迟检索策略:load 方法支持延迟加载策略。而 get 不支持。
解释下何为延迟加载:
    当执行测试代码,
load与get同时执行都能获得结果
User user2=(User) session.load(User.class, 1);
User user2=(User) session.get(User.class, 1);
System.out.println(user2);

结果图




但是如果只写下面而不输出,你会发现get会输出sql语句而load不会,
User user2=(User) session.load(User.class, 1);
User user2=(User) session.get(User.class, 1);
而且当你去输出System.out.println(user2.getClass().getName());你会发现load只是个代理对象
  •   当你在执行过程中突然关闭session你再输出,get照样能获得对象.但是load会抛出AbstractLazyInitializer异常
User user2=(User) session.load(User.class, 1);
session.close();
System.out.println(user2);


(三)update()
Session 的 update() 方法使一个游离对象--->持久化对象, 并且计划执行一条 update 语句.
若希望 Session 仅当修改了 News 对象的属性时, 才执行 update() 语句, 可以把映射文件中 <class> 元素的select-before-update设为 true. 该属性的默认值为 false
当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常. 

  这种异常不注意很容易犯,理解原理就知道了
下面是一段获取user和修改user的代码
                Configuration cfg = new Configuration().configure("hibernatel.cfg.xml");
		SessionFactory factory = cfg.buildSessionFactory();
		Session session = factory.openSession();
		Transaction ts = session.beginTransaction();
                User u = (User) session.get(User.class, 1);
                session.getTransaction().commit();
                System.out.println(u);// 获得第一个用户
                session.update(new User(1,"刘德华","ldh"));//修改
                ts.commit();
                session.close();
	        factory.close();
运行会出现以下错误
 org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session 

错误原因: 是说主键不唯一,在事务的最后执行SQL时,session缓存里面有多个(>1)主键一样的对象。
了解过hibernate的都知道它有一个一级缓存,即session级别的缓存,在一个事务的执行过程中可以管理持久化对象,在事务最后执行SQL,可以减少数据库的操作。
在同一个session内,如果已经有一个对象已经是持久化状态(load进来等),现在构造一个新的PO,和前一个持久化对象拥有相同的持久化标识(identifier),在update的时候,就会抛这个错误

解决措施:
    1.我们可以用merge()方法来代替update()方法
这两者的区别在于:
      update():如果你确定在当前session的会话里不存在一个与要进行update操作有相同标识符(主键)的持久化对象,那么调用update()。     
  merge():如果你在任何时候修改了数据都想把数据保存到数据库中,那么就调用merge()。
    2.先清理缓存 session.clear()

(四)saveOrUpdate方法
就是save与update的方法的结合

(五)delete()方法

Session 的 delete() 方法既可以删除一个游离对象, 也可以删除一个持久化对象
Session 的 delete() 方法处理过程
计划执行一条 delete 语句
把对象从 Session 缓存中删除, 该对象进入删除状态.
Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true, 将改变 delete() 方法的运行行为: delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象

(6)evict()方法
移除session缓存指定的持久化对象
做个测试:
   
结果是只发送了一条sql语句,第二次直接从缓存中找,但是当你移除对象后你会发现会发送两条sql语句

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值