hibernate与jpa中的session.flush和commit的区别

今天oracle的SQL plus 中执行了删除查询操莋然后PL/SQL中也执行查询操作,语句一样结果却不一样,让我大感郁闷后来才突然想到可能是两边数据不一致造成的,但是为什么不┅致呢就是没用commit网上...

一、hibernate与jpa是一种对象关系映射框架

JNDI(Java Naming and Directory Interface,Java命名和目录接口 )提供了一种统一的方式可以用在网络上查找和访问服务。通过指定一个资源名称该名称对应于

或命名服务中的一个记錄,同时返回数据库连接建立所必须的信息

编写的类和接口组成。JDBC提供了一种基准据此可以构建更高级的工具和接口,使数据库开发囚员能够编写数据库应用程序

JPA 是在hibernate与jpa作者参与的情况下完成的一个规范

JPA是一个标准,可以看成是一个接口旗下统领着N多个具体框架。

嘚基本特征是完全面向对象的程序设计语言到关系的 映射,

Session是hibernate与jpa运作的中心对象的生命周期、事务的管理、数据库的存取都与 Session息息相关,

而Session並非线程安全,也就是说如果多个线程同时使用一个Session实例进行数据存取,则将会导致 Session 数据存取逻辑混乱.

因此创建的Session实例必须在本地存取涳上运行使之总与当前的线程相关。 这里就需要用到ThreadLocal,在很多种Session 管理方案中都用到了它.

一种较为特殊的线程绑定机制通过ThreadLocal存取的数据,總是与当前线程相关也就是说,JVM 为每个运行的线程绑定了私有的本地实例存取空间

从而为多线程环境常出现的并发访问问题提供了┅种隔离机制,ThreadLocal并不是线程本地化的 实现,而是线程局部变量

也就是说每个使用该变量的线程都必须为该变量提供一个副本,每个线程改变该變量的值仅仅是改变该副本的值,而不会影响其他线程的 该变量的值,ThreadLocal是隔离多个线程的数据共享,

不存在多个线程之间共享资源,因此不再需偠对线程同步

 session在多线程下不安全,当两个应用程序拿到同一个session对象时就会出现脏读和死锁问题

sessionFactory是重量级的,线程安全也叫一级缓存
session昰轻量级的,线程不安全也叫二级缓存

Session的使用原则就是一个线程一个session,session一次一个事务解决session线程不安全的二种解决方案:

以上两种解决方案有一点不同就是用第二种方式时,当我们的事物提交后session会自动关闭,而用第一种方式需要我们手动关session

 SessionFactory:线程安全保存了当前的数據库配置信息和所有映射关系以及预定义的SQL语句。在SessionFactory中内置了连接池其有两个常用方法:

* 如果使用的是本地事务(jdbc事务)

* 如果使用的是铨局事务(jta事务)

        在同步机制中,使用同步保证同一时间只有一个线程访问不能同时访问共享资源,否则就是出现错误ThreadLocal则隔离了相关嘚资源,并在同一个线程中可以共享这个资源彼此独立,修改不会影 响到对方

 对于多线程资源共享问题,同步机制采用了“以时间换涳间”的方式而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量让不同的线程排队访问,而后者为每一个线程都提供了一份变量因此可以同时访问而互不影响。

关于loadget方法区别的说明:

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

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

  * a):首先通过idsession缓存中查找对象如果存在此id的对象,直接将其返回
  * b):茬二级缓存中查找找到后将 其返回。
  * c):如果在session缓存和二级缓存中都找不到此对象则从数据库中加载有此ID的对象
  * 因此get()方法并不总是导致SQL語句,只有缓存中无此数据时才向数据库发送SQL  

  * 1:在立即加载对象(当hibernate与jpa在从数据库中取得数据组装好一个对象后
  * 会立即再从数据库取嘚数据此对象所关联的对象)时,如果对象存在
  * load()get()方法没有区别,都可以取得已初始化的对象;但如果当对
  * 抛出一个异常因此使用load()方法時,要确认查询的主键ID一定是存在
  * 的从这一点讲它没有get方便!
  * 2:在延迟加载对象(hibernate与jpa从数据库中取得数据组装好一个对象后,
  * 不会立即再從数据库取得数据组装此对象所关联的对象而是等到需要时,
  * 都会从数据库取得数据组装此对象关联的对象)get()方法仍然使用
  * 立即加载嘚方式发送SQL语句,并得到已初始化的对象而load()方法则
  * 根本不发送SQL语句,它返回一个代理对象直到这个对象被访问时才被

直接的数据库操作有着很多的不哃正因为我们对这种方式没有一个大致的了解造成了我们的应用并未得到预先设想的结果。
那hibernate与jpa的持久化机制到底有什么不同那简单嘚说,hibernate与jpa在数据库层之上实现了一个缓存区当应用save或者update一个 对象时,hibernate与jpa并未将这个对象实际的写入数据库中而仅仅是在缓存中根据应鼡的行为做了登记,在真正需要将缓存中的数据flush入数据库时 才执行先前登记的所有行为
在实际执行的过程中,每个Session是通过几个映射和集匼来维护所有与该Session建立了关联的对象以及应用对这些对象所进行的操作的与我们这 次讨论有关的有entityEntries(与Session相关联的对象的映射)、insertions(所有嘚插入操作集合)、deletions (删除操作集合)、updates(更新操作集合)。下面我就开始解释在最开始的例子中hibernate与jpa到底是怎样运作的。
(1)生成一个事务嘚对象并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。
(2)应用使用s.save保存cat对象这个时候Session将cat这个对象放入entityEntries,用来标记cat已经囷当前的会话建 立了关联由于应用对cat做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、 Session、持久化处理類)
(4)事务提交,需要将所有缓存flush入数据库Session启动一个事务,并按照insert,update,......,delete的顺序 提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update這里的特殊处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺 序要善于使用flush),现在cat不在entityEntries中但在执行insert的行为时只需要访問insertions就足够了,所以 此时不会有任何的异常异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries中cat的 我想现在大家應该明白例子中的程序到底哪里有问题了吧我们的错误的认为s.save会立即的执行,而将cat对象过早的与Session拆离造成了 Session的insertions和entityEntries中内容的不同步。所鉯我们在做此类操作时一定要清楚hibernate与jpa什么时候会将数 据flush入数据库在未flush之前不要将已进行操作的对象从Session上拆离。
对于这个错误的解决方法昰我们可以在(2)和(3)之间插入一个s.flush()强制Session将缓存中的数据flush入数据库(此时 hibernate与jpa会提前启动事务,将(2)中的save登记的insert语句登记在数据库事务中并将所囿操作集合清空),这样在(4)事务提交时 insertions集合就已经是空的了即使我们拆离了cat也不会有任何的异常了。
前面简单的介绍了一下hibernate与jpa的flush机制和對我们程序可能带来的影响以及相应的解决方法hibernate与jpa的缓存机制还会在其他的方面给我们的程序带来一些意想不到的影响。看下面的例子:
这个例子看起来有什么问题估计不了解hibernate与jpa缓存机制的人多半会说没有问题,但它也一样不能按照我们的思路正常运行在flush过程中会产 苼主键冲突,可能你想问:"在save(littleCat)之前不是已经更改cat.name并已经更新了么为什么还会发生主键冲突那?"这里的原因 flush才会继续flush的过程中首先要执荇最开始的save动作,在这个save中检查了cat这个对象的id是否与原来执行动作时的id相同 不幸的是,此时cat的id被赋为null异常被抛出,程序终止(此处要紸意我们在以后的开发过程尽量不要在flush之前改变已经进行了操作的对象 的id)。
这个例子中的错误也是由于缓存的延时更新造成的(当然与不正规的使用hibernate与jpa也有关系),处理方法有两种:
1、在(8)之后flush这样就可以保证(10)处save将cat作为一个全新的对象进行保存。
2、删除(9)这样第二次save所引起的强制flush可以正常的执行,在数据库中插入cat对象后将其删除然后继续第二次save重新插入cat对象,此时cat的id仍与从前一致
这兩种方法可以根据不同的需要来使用,呵呵总觉得好像是很不正规的方式来解决问题,但是问题本身也不够正规只希望能够在应用开發中给大家一些帮助,不对的地方也希望各位给与指正
总的来说,由于hibernate与jpa的flush处理机制我们在一些复杂的对象更新和保存的过程中就要栲虑数据库操作顺序的改变以及延时flush是 否对程序的结果有影响。如果确实存在着影响那就可以在需要保持这种操作顺序的位置加入flush强制hibernate與jpa将缓存中记录的操作flush入 数据库,这样看起来也许不太美观但很有效。

我要回帖

更多关于 hibernate与jpa 的文章

 

随机推荐