关于在使用hibernate在提交事务时常遇到的异常:java
an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session数据库
其实这个异常通常都是和咱们在操做session flush方法和提交事务过程当中会抛出的,下面就具体结合session的事务和声明周期来具体分析下,为何会有这样的异常;缓存
首先来看下,session的生命周期session
Hibernate中java对象的三种状态:ui
一、临时状态(transient):用new语句建立,尚未被持久化,不处于Session的缓存中。 this
二、持久化状态(persistent):已使用save()或者saveOrUpdate()方法,处于Session的缓存中和数据库表中,生成了本身的Oid标识。 hibernate
三、游离状态(detached):被持久化,已使用evict(Object),session.close()或者使用clear()清除缓存,再也不处于Session的缓存中或不存在数据库表中,可是依然是存在本身的OId标识。 对象
对象的状态转换blog
从上面的图中咱们能够很清楚的明白一个java对象在session中三种状态的转换,生命周期
而后在来看看session缓存在何时会被清除:
1.当应用程序调用org.hibernate.Transaction的commit()方法的时候,commit()方法先清理缓存,而后再向数据库提交事务。
2.当应用程序显式调用Session的flush()方法的时候,其实这个方法咱们几乎不多用到,由于咱们通常都是在完成一个事务才去清理缓存,提交数据更改,这样咱们直接提交事务就能够。
clear()和evict(Object)的区别:
从参数就能够看出,clear()是会清除整个session中的缓存,evict(Object)是将一个对象从session缓存中清除;
其实在session持久化操做和数据库中之间还有一层对象缓冲区(entityEntries)
Commit():此方法在执行后会更新对象在对象缓存区中的existsInDatabase=true;
Flush():会按save,update,delete顺序执行,把缓存中的数据flush入数据库中,并清空缓存区;
下面几个例子能够充分说明咱们异常抛出的状况:
SessionFactory sf = new Configuration().configure().buildSessionFactory() ;
Session s = sf.openSession();
Person person = new Person();
Transaction tran = s.beginTransaction(); (1)
s.save(person); (2)(此处一样能够为update delete)
s.evict(person); (3)
tran.commit(); (4)
s.close();(5)
看上面的代码,再参照下咱们的示例图和commit()方法,就能够很明显的发现代码问题的所在,在第四步evict()方法将cat对象从对象缓存区清除,当咱们执行commit()方法后,更新对象在缓存区中状态的时候,因为已被清除,就会出现上述断言的异常;
Person person1 = new Person ();
person1.setName(“tom”);
s.save(person1);
person1.setName(“mary”);
s.update(person1);
Person person2 = new Person ();
person2.setName(“tom”);
s.save(person2);
s.flush();
其实在这里咱们看这个代码的时候感受是没问题 ,在这里咱们能够参考下刚提到的flush()方法,此方法会按save,update,delete的顺序进行提交事务,因此在这里会抛出主键冲突的异常,解决的办法是在update()操做后面也加入flush();
总的来讲,因为flush()的特殊处理机制,虽然不建议使用此方法,可是在一些复杂的事务处理过程当中,加入此方法虽然会破坏事务的一个提交的完整性,可是能够规避一些不可预见的异常状况!