理解hibernat的flush(转)

*SessionFlush:

*session flush方法主要做了两件事:

     清理临时记录

     执行sql

*session在什么情况下执行flush

     默认在事务提交时(commit)

     显示的调用flush

     在执行查询前,如:iterate

hibernate按照save(insert),update、delete顺序提交相关操作

!!只要事务没有commit,就随时可以回滚 !!

所有状态都适用:

flush:清的是临时记录!!!!!

elementData临时数据,entryArray缓存数据:只有在save后才有数据!!与三个状态有关

调用commit的时候默认会先执行flush()

uuid:commit时的flush清理临时记录

save后:Hiernate就分配一个32的字符串,数据库中没有记录,elementData临时记录:有数据,entryArray缓存中:有记录,existsInDatabase:flase,纳入了session管理.

【如果此时执行:session.evict(user);//逐出缓存,清的是缓存中的记录,但临时集合没有清掉. 然后再执行commit,flush时发现临时集合有数据,就会发insert,然后找缓存记录,把existsInDatabase状态变为true,因为缓存清掉了(existsInDatabase也没了),所以有问题,抛异常】【上述问题的解决:save()完后执行flush(发sql,清临时集合,找缓存,改变esistsInDatabase状态),然后执行逐出,清理缓存,再执行commit,找临时集合有没数据,有的话发sql,已经flush了,所以没数据,不发sql】

flush后:清理临时集合,发sql,existsInDatabasse:true,临时集合没有数据了,但是由于隔离级别,MySQL数据库是查不到的。缓存中有记录。

commit后:数据不可回滚了,commit默认会先执行flush();所以不用显示调用

native:save清临时记录

save后:执行insert语句,清理临时记录。返回由数据库生成的id,纳入session管理因为缓存的map中有记录了,其中:id是由数据库返回的。临时数据中没记录(永远也看不到:save前没有save后也没有,只有save过程中才有,一旦发了sql临时记录中就没有记录了)

【此时执行session.evict(user),没问题,与uuid的区别!!!】

flush后:临时记录在save的时候已经清了

commit后:数据不可回滚了

不管什么策略:执行commit()默认会先执行flush(),commit()后数据无法回滚!!

assigned:flush情况和uuid相同,因为id new对象的时候给出了,不用数据库里取.

                            Uuid是Hibernate自动生成了,也不用数据库里取

public void testSave3(){

       Session session = null;

       Transaction tx = null;

      

       try{

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           //Transient 瞬时状态

           User3 user = new User3();

           user.setId("0001");

           user.setName("张三");

          

           session.save(user);

          

           user.setName("王五");

           session.update(user);//此行可以不写,因为此时为持久状态,Hibernate会自动同步

          

           User3 user3 = new User3();

           user3.setId("002");

           user3.setName("李四");

           session.save(user3);

           System.out.print("hell");

           tx.commit();//此时发sql,hibernate按照save(insert),update、delete顺序提交相关操作,而上面的顺序是:保存、更新、保存。发的顺序发insert,insert,update。//如果在user.setName("王五");前先执行session.flush();则先发两条sql,然后最后commit的时候再发一条insert。所有,发sql的顺序按我们的意愿执行。 

       }catch(Exception e){

           e.printStackTrace();

           tx.rollback();

       }finally{

           HibernateUtils.closeSession(session);

       }  

    }

数据库隔离级别:解决并发性问题

隔离级别                                             是否存在脏读        是否存在不可重复读    是否存在幻读

Read Unommited(未提交读)                           Y                   Y                           Y

Read Commited(提交读,Oracle默认的)              N             Y 与悲观锁有关               Y

Repeatable Read(可重复读,MySql默认)           N                    N                         Y

Serialiazble(序列化读)                                    N                   N                          N

隔离级别低到高:MySQL隔离级别高于Oracle

序列化读:并发性最差,一个人独占!

脏读:没有提交数据就读到

不可重复读:第一次读出来为张三,再刷新一次为李四,悲观锁锁住就变成可重复读了

存在幻读:影响的是插入操作。A读出5条记录,刷新变成十条记录,新增了5条。

序列化读:锁住表,别人不可能添加数据。

查看mysql的隔离级别: select @@tx_isolation; 默认级别可重复读,必须提交了才可以看得到

改隔离级别为未提交读:set transaction  isolation level read uncommitted

package com.guojie.hibernate;

import java.util.Date;

import org.hibernate.Session;

import org.hibernate.Transaction;

import com.guojie.hibernate.HibernateUtils;

import com.guojie.hibernate.User1;

import com.guojie.hibernate.User2;

import com.guojie.hibernate.User3;

import junit.framework.TestCase;

public class SessionFlushTest extends TestCase {

   

    /**

     * 测试uuid主键生成策略

     */

    public void testSave1() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User1 user = new User1();

           user.setName("李四");

           user.setPassword("123");

           user.setCreateTime(new Date());

           user.setExpireTime(new Date());

          

           //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理

           //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false

           session.save(user);

          

           //调用flush,hibernate会清理缓存,执行sql

           //如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据

           //并且session中existsInDatebase状态为true

           session.flush();

          

           //提交事务

           //默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush

           //commit后数据是无法回滚的

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }

   

    /**

     * 测试native主键生成策略

     */

    public void testSave2() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User2 user = new User2();

           user.setName("张三1");

           user.setPassword("123");

           user.setCreateTime(new Date());

           user.setExpireTime(new Date());

          

           //因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id

           //纳入了session的管理,修改了session中existsInDatebase状态为true

           //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据

           session.save(user);

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }

   

   

    /**

     * 测试uuid主键生成策略

     */

    public void testSave3() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User1 user = new User1();

           user.setName("王五");

           user.setPassword("123");

           user.setCreateTime(new Date());

           user.setExpireTime(new Date());

          

           //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理

           //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false

           session.save(user);

          

           //将user对象从session中逐出,即session的EntityEntries属性中逐出

           session.evict(user);

          

           //无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后

           //需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries

           //中逐出了,所以找不到相关数据,无法更新,抛出异常

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }

   

    /**

     * 测试uuid主键生成策略

     */

    public void testSave4() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User1 user = new User1();

           user.setName("王五");

           user.setPassword("123");

           user.setCreateTime(new Date());

           user.setExpireTime(new Date());

          

           //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理

           //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false

           session.save(user);

          

           //flush后hibernate会清理临时集合,会将user对象保存到数据库中,将session中的insertions中的user对象

           //清除,并且设置session中existsInDatebase的状态为true

           session.flush();

          

           //将user对象从session中逐出,即session的EntityEntries属性中逐出

           session.evict(user);

          

           //可以成功提交,因为hibernate在清理临时集合时,在session的insertions集合中无法找到user对象

           //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }

   

    /**

     * 测试native主键生成策略

     */

    public void testSave5() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User2 user = new User2();

           user.setName("张三11");

           user.setPassword("123");

           user.setCreateTime(new Date());

           user.setExpireTime(new Date());

          

           //因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id

           //纳入了session的管理,修改了session中existsInDatebase状态为true

           //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据

           session.save(user);

          

           //将user对象从session中逐出,即session的EntityEntries属性中逐出

           session.evict(user);

          

           //可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象

           //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }

   

    /**

     * 测试assigned主键生成策略

     *

     */

    public void testSave6() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User3 user = new User3();

           user.setId("001");

           user.setName("张三");

          

           session.save(user);

          

           user.setName("王五");

           session.update(user);

          

           User3 user3 = new User3();

           user3.setId("002");

           user3.setName("李四");

           session.save(user3);

          

           //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)

           //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)

           //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?

           //hibernate按照save(insert),update、delete顺序提交相关操作

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }  

   

    /**

     * 测试assigned主键生成策略

     *

     */

    public void testSave7() {

       Session session = null;

       Transaction tx = null;

       try {

           session = HibernateUtils.getSession();

           tx = session.beginTransaction();

           User3 user = new User3();

           user.setId("003");

           user.setName("张三");

          

           session.save(user);

          

           user.setName("王五");

           session.update(user);

          

           session.flush();

          

           User3 user3 = new User3();

           user3.setId("004");

           user3.setName("李四");

           session.save(user3);

          

           //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)

           //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?

           //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)

           //因为我们在session.udpate(user)后执行了flush,所以在清理缓存时执行flush前的sql不会生成

           //sql会按照我们的意愿执行

           tx.commit();

       }catch(Exception e) {

           e.printStackTrace();

           tx.rollback();

       }finally {

           HibernateUtils.closeSession(session);

       }

    }     

}

 

原文地址:https://www.cnblogs.com/Fskjb/p/1625050.html