Hibernate的evict(obj) clear() flush()

问:
先创建一个Student,然后调用session.save方法,然后再调用evict方法把Student对象清除出缓存,再提交事务,
可是会报错:Exception in thread "main" org.hibernate.AssertionFailure: possible nonthreadsafe access to session
但是如果我用的不是evict方法,而是clear方法用来清除缓存的话,程序没有错。

答:
session.evict(obj),从session缓存(EntityEntries属性)中逐出该对象
session.clear(),把缓冲区内的全部对象清除,但不包括操作中的对象

所以,hibernate执行的顺序如下,

(1)生成一个事务的对象,并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。
(2)应用使用s.save保存对象,这个时候Session将这个对象放入entityEntries,用来标记对象已经
和当前的会话建立了关联,由于应用对对象做了保存的操作,Session还要在insertions中登记应用
的这个插入行为(行为包括:对象引用、对象id、Session、持久化处理类)。
(3)s.evict将对象从s会话中拆离,这时s会从entityEntries中将这个对象移出。
(4)事务提交,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert,update,……,
delete的顺序提交所有之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊
处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺序,要善于使用flush),现在对象不
在entityEntries中,但在执行insert的行为时只需要访问insertions就足够了,所以此时不会有任何
的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries
中对象的existsInDatabase标志置为true,由于对象并不存在于entityEntries中,此时Hibernate就
认为insertions和entityEntries可能因为线程安全的问题产生了不同步(也不知道Hibernate的开发者
是否考虑到例子中的处理方式,如果没有的话,这也许算是一个bug吧),于是一个net.sf.hibernate.AssertionFailure
就被抛出,程序终止

一般错误的认为s.save会立即的执行,而将对象过早的与Session拆离,造成了Session的insertions
和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数据
flush入数据库,在未flush之前不要将已进行操作的对象从Session上拆离。
解决办法是在save之后,添加session.flush。

  1. 但是与commit同时使用,会抛出异常   
  2.             session = HibernateUtils.getSession();   
  3.             tx = session.beginTransaction();   
  4.        
  5.             User1 user = new User1();   
  6.             user.setName("李四");   
  7.             user.setPassword("123");   
  8.             user.setCreateTime(new Date());   
  9.             user.setExpireTime(new Date());   
  10.                
  11.             //利用Hibernate将实体类对象保存到数据库中   
  12.             //因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理   
  13.             //不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false   
  14.             session.save(user);   
  15.                    
  16.             session.evict(user);//从session缓存(EntityEntries属性)中逐出该对象   
  17.         //无法成功提交,因为hibernate在清理缓存时,在session的临时集合(insertions)中取出user对象进行insert操作后需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session中逐出了,所以找不到相关数据,无法更新,抛出异常。   
  18.                
  19.             tx.commit();   
  20.   
  21. 解决在逐出session缓存中的对象不抛出异常的方法:   
  22. 在session.evict()之前进行显示的调用session.flush()方法就可以了。   
  23.         //利用Hibernate将实体类对象保存到数据库中   
  24.             //因为user主键生成策略采用的是uuid,所以调用完成save后,只是将user纳入session的管理   
  25.             //不会发出insert语句,但是id已经生成,session中的existsInDatabase状态为false   
  26.             session.save(user);   
  27.                    
  28.             //flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象清除,并且会设置session中的existsInDatabase状态为true
  29.             session.flush();   
  30.                
  31.             session.evict(user);//从session缓存(EntityEntries属性)中逐出该对象   
  32.                
  33.             //可以成功提交,因为hibernate在清理缓存时,在Session的insertions中集合中无法找到user对象所以不会发出insert语句,也不会更新session中existsInDatabase的状态。   
  34.             tx.commit();  



session.evict(obj),会把指定的缓冲对象进行清除

session.clear(),把缓冲区内的全部对象清除,但不包括操作中的对象

隔离级别  脏读 不可重复读 幻读 

ReadUncommitted  Y Y Y 
ReadCommitted N Y Y 
RepeatableRead N N Y 
Serializable N N N 

ReadCommited是oracle的默认隔离级别。可以通过悲观锁,消除不可重复读。 
RepeatableRead是Mysql的默认级别。 


session flush方法主要做了两件事: 
* 清理缓存 
* 执行sql 

session在什么情况下执行flush 
* 默认在事务提交时 
* 显示的调用flush 
* 在执行查询前,如:iterate 

hibernate按照save(insert),update、delete顺序提交相关操作 
Java代码  收藏代码
  1. /**   
  2.      * 测试uuid主键生成策略   
  3.      */    
  4.     public void testSave1() {     
  5.         Session session = null;     
  6.         Transaction tx = null;     
  7.         try {     
  8.             session = HibernateUtils.getSession();     
  9.             tx = session.beginTransaction();     
  10.     
  11.             User1 user = new User1();     
  12.             user.setName("李四");     
  13.             user.setPassword("123");     
  14.             user.setCreateTime(new Date());     
  15.             user.setExpireTime(new Date());     
  16.                  
  17.             //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理     
  18.             //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false     
  19.             session.save(user);     
  20.                  
  21.             //调用flush,hibernate会清理缓存,执行sql     
  22.             //如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据     
  23.             //并且session中existsInDatebase状态为true     
  24.             session.flush();     
  25.                  
  26.             //提交事务     
  27.             //默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush     
  28.             //commit后数据是无法回滚的     
  29.             tx.commit();     
  30.         }catch(Exception e) {     
  31.             e.printStackTrace();     
  32.             tx.rollback();     
  33.         }finally {     
  34.             HibernateUtils.closeSession(session);     
  35.         }     
  36.     }     
  37.          
  38.     /**   
  39.      * 测试native主键生成策略   
  40.      */    
  41.     public void testSave2() {     
  42.         Session session = null;     
  43.         Transaction tx = null;     
  44.         try {     
  45.             session = HibernateUtils.getSession();     
  46.             tx = session.beginTransaction();     
  47.     
  48.             User2 user = new User2();     
  49.             user.setName("张三1");     
  50.             user.setPassword("123");     
  51.             user.setCreateTime(new Date());     
  52.             user.setExpireTime(new Date());     
  53.                  
  54.             //因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id     
  55.             //纳入了session的管理,修改了session中existsInDatebase状态为true     
  56.             //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据     
  57.             session.save(user);     
  58.             tx.commit();     
  59.         }catch(Exception e) {     
  60.             e.printStackTrace();     
  61.             tx.rollback();     
  62.         }finally {     
  63.             HibernateUtils.closeSession(session);     
  64.         }     
  65.     }     
  66.          
  67.          
  68.     /**   
  69.      * 测试uuid主键生成策略   
  70.      */    
  71.     public void testSave3() {     
  72.         Session session = null;     
  73.         Transaction tx = null;     
  74.         try {     
  75.             session = HibernateUtils.getSession();     
  76.             tx = session.beginTransaction();     
  77.     
  78.             User1 user = new User1();     
  79.             user.setName("王五");     
  80.             user.setPassword("123");     
  81.             user.setCreateTime(new Date());     
  82.             user.setExpireTime(new Date());     
  83.                  
  84.             //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理     
  85.             //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false     
  86.             session.save(user);     
  87.                  
  88.             //将user对象从session中逐出,即session的EntityEntries属性中逐出     
  89.             session.evict(user);     
  90.                  
  91.             //无法成功提交,因为hibernate在清理缓存时,在session的insertions集合中取出user对象进行insert操作后     
  92.             //需要更新entityEntries属性中的existsInDatabase为true,而我们采用evict已经将user从session的entityEntries     
  93.             //中逐出了,所以找不到相关数据,无法更新,抛出异常     
  94.             tx.commit();     
  95.         }catch(Exception e) {     
  96.             e.printStackTrace();     
  97.             tx.rollback();     
  98.         }finally {     
  99.             HibernateUtils.closeSession(session);     
  100.         }     
  101.     }     
  102.          
  103.     /**   
  104.      * 测试uuid主键生成策略   
  105.      */    
  106.     public void testSave4() {     
  107.         Session session = null;     
  108.         Transaction tx = null;     
  109.         try {     
  110.             session = HibernateUtils.getSession();     
  111.             tx = session.beginTransaction();     
  112.     
  113.             User1 user = new User1();     
  114.             user.setName("王五");     
  115.             user.setPassword("123");     
  116.             user.setCreateTime(new Date());     
  117.             user.setExpireTime(new Date());     
  118.                  
  119.             //因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理     
  120.             //不会发出insert语句,但是id已经生成,session中existsInDatebase状态为false     
  121.             session.save(user);     
  122.                  
  123.             //flush后hibernate会清理缓存,会将user对象保存到数据库中,将session中的insertions中的user对象     
  124.             //清除,并且设置session中existsInDatebase的状态为true     
  125.             session.flush();     
  126.                  
  127.             //将user对象从session中逐出,即session的EntityEntries属性中逐出     
  128.             session.evict(user);     
  129.                  
  130.             //可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象     
  131.             //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态     
  132.             tx.commit();     
  133.         }catch(Exception e) {     
  134.             e.printStackTrace();     
  135.             tx.rollback();     
  136.         }finally {     
  137.             HibernateUtils.closeSession(session);     
  138.         }     
  139.     }     
  140.          
  141.     /**   
  142.      * 测试native主键生成策略   
  143.      */    
  144.     public void testSave5() {     
  145.         Session session = null;     
  146.         Transaction tx = null;     
  147.         try {     
  148.             session = HibernateUtils.getSession();     
  149.             tx = session.beginTransaction();     
  150.     
  151.             User2 user = new User2();     
  152.             user.setName("张三11");     
  153.             user.setPassword("123");     
  154.             user.setCreateTime(new Date());     
  155.             user.setExpireTime(new Date());     
  156.                  
  157.             //因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id     
  158.             //纳入了session的管理,修改了session中existsInDatebase状态为true     
  159.             //如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据     
  160.             session.save(user);     
  161.                  
  162.             //将user对象从session中逐出,即session的EntityEntries属性中逐出     
  163.             session.evict(user);     
  164.                  
  165.             //可以成功提交,因为hibernate在清理缓存时,在session的insertions集合中无法找到user对象     
  166.             //所以就不会发出insert语句,也不会更新session中的existsInDatabase的状态     
  167.             tx.commit();     
  168.         }catch(Exception e) {     
  169.             e.printStackTrace();     
  170.             tx.rollback();     
  171.         }finally {     
  172.             HibernateUtils.closeSession(session);     
  173.         }     
  174.     }     
  175.          
  176.     /**   
  177.      * 测试assigned主键生成策略   
  178.      *    
  179.      */    
  180.     public void testSave6() {     
  181.         Session session = null;     
  182.         Transaction tx = null;     
  183.         try {     
  184.             session = HibernateUtils.getSession();     
  185.             tx = session.beginTransaction();     
  186.     
  187.             User3 user = new User3();     
  188.             user.setId("001");     
  189.             user.setName("张三");     
  190.                  
  191.             session.save(user);     
  192.                  
  193.             user.setName("王五");     
  194.             session.update(user);     
  195.                  
  196.             User3 user3 = new User3();     
  197.             user3.setId("002");     
  198.             user3.setName("李四");     
  199.             session.save(user3);     
  200.                  
  201.             //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)     
  202.             //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)     
  203.             //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?     
  204.             //hibernate按照save(insert),update、delete顺序提交相关操作     
  205.             tx.commit();     
  206.         }catch(Exception e) {     
  207.             e.printStackTrace();     
  208.             tx.rollback();     
  209.         }finally {     
  210.             HibernateUtils.closeSession(session);     
  211.         }     
  212.     }        
  213.          
  214.     /**   
  215.      * 测试assigned主键生成策略   
  216.      *    
  217.      */    
  218.     public void testSave7() {     
  219.         Session session = null;     
  220.         Transaction tx = null;     
  221.         try {     
  222.             session = HibernateUtils.getSession();     
  223.             tx = session.beginTransaction();     
  224.     
  225.             User3 user = new User3();     
  226.             user.setId("003");     
  227.             user.setName("张三");     
  228.                  
  229.             session.save(user);     
  230.                  
  231.             user.setName("王五");     
  232.             session.update(user);     
  233.                  
  234.             session.flush();     
  235.                  
  236.             User3 user3 = new User3();     
  237.             user3.setId("004");     
  238.             user3.setName("李四");     
  239.             session.save(user3);     
  240.                  
  241.             //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)     
  242.             //Hibernate: update t_user3 set name=?, password=?, create_time=?, expire_time=? where user_id=?     
  243.             //Hibernate: insert into t_user3 (name, password, create_time, expire_time, user_id) values (?, ?, ?, ?, ?)     
  244.             //因为我们在session.udpate(user)后执行了flush,所以在清理缓存时执行flush前的sql不会生成     
  245.             //sql会按照我们的意愿执行     
  246.             tx.commit();     
  247.         }catch(Exception e) {     
  248.             e.printStackTrace();     
  249.             tx.rollback();     
  250.         }finally {     
  251.             HibernateUtils.closeSession(session);     
  252.         }     
  253.     }    



Hibernate的flush机制:http://sind.iteye.com/blog/255429
posted @ 2016-06-03 16:40  He_quotes  阅读(284)  评论(0)    收藏  举报