背景:java
一个偶然的机会,我作了一个例子,中间我遇到了一个有意思的问题,就是在执行commit方法以前,作了两次save操做,以下:mysql
这样执行以后,报了一个:关于session主键冲突的异常,这以前,我也见过一个,就是整棵继承树映射一张表的时候,主键自增加不配置时,这个,我没有弄啊!而后开始这种查找资料,后来知道,commit方法会隐形的调用一个flush方法,而这个方法不是按照咱们代码来提交事务的,而是会按着save、update、delete这个顺序来提交事务,因此会连续有两个save变成持久化对象,而形成session生成的Oid重复。web
也就是由于这个,我对flush感了兴趣,下面咱们介绍一下关于flush的那些事:sql
一、flush有什么用?数据库
调用flush方法,会按save,update,delete顺序执行,把缓存中的数据flush入数据库中,并清空缓存区;这里注意啊,这里只是将缓存中的数据同步到数据库中,这里并无在数据库中insert数据,能够理解为把缓存内数据同步到一张临时表内(缓存区),这时候,直接查询数据库是没有新添数据的,可是使用发送sql语句查询却能够查出数据来(前提:数据库隔离级别要为 未提交读),当提交事务时,数据库事务管理器提交事务执行提交过来的sql语句,修改数据库数据。也就是说,只有commit才会更改数据库数据。缓存
整理一下啊:flush方法的主要做用就是清理缓存,强制数据库与hibernate缓存同步,以保证数据的一致性。其实在session持久化操做和数据库中之间还有一层对象缓冲区session
flush的主要动做就是向数据库发送一系列的sql语句,并执行这些sql语句,可是不会向数据库提交。而commit方法则会首先调用flush方法,而后提交事务。这就是为何咱们仅仅调用flush的时候记录并未插入到数据库中的缘由,由于只有提交了事务,对数据库所作的更新才会被保存下来。由于commit方法隐式的调用了flush,因此通常咱们都不会显示的调用flush方法。app
二、关于主键生成策略的一点隐私:oop
当你的id主键生成策略为native,调用session.save后,将执行insert语句,返回有数据库生成的id 归入了session的管理,修改了session中existsInDatabase状态为true,若是数据库的隔离级别设置为未提交读,那么咱们能够看到save过的数据 ;ui
当id主键生成策略采用的是uuid,调用完成save后,只是将user归入到了session的管理,不会发出insert语句,可是id已经生成,session中existsInDatabase状态为false;
三、最后那如何解决上面的问题呢,很简单,在update以后,执行session.flush,而后再第二个save以后,再执行一次flush就能够,
这样就能按照咱们的想法顺序缓存sql语句了有木有?
四、总结:
因为flush()的特殊处理机制,虽然不建议使用此方法,可是在一些复杂的事务处理过程当中,加入此方法虽然会破坏事务的一个提交的完整性,可是能够规避一些不可预见的异常状况!