由openSession、getCurrentSession和HibernateDaoSupport浅谈Spring对事物的支持
Spring和Hibernate的集成的一个要点就是对事务的支持,openSession、getCurrentSession都是编程式事务(手动设置事务的提交、回滚)中重要的对象,HibernateDaoSupport则提供了更方便的声明式事务支持。
Hibernate中最重要的就是Session对象的引入,它是对jdbc的深度封装,包括对事务的处理,Session对象通过SessionFactory来管理,openSession和getCurrentSession是管理session的重要的方法。
openSession和getCurrentSession的根本区别在于有没有绑定当前线程,所以,使用方法有差异:
* openSession没有绑定当前线程,所以,使用完后必须关闭,
* currentSession和当前线程绑定,在事务结束后会自动关闭。
关于事务的边界和传播:
通常情况下事务的边界需要设置在业务逻辑处理层中,但是,如果在一个业务中涉及到多个业务逻辑层之间的方法,且需要在同一个事务中运行,那么,这就涉及到了事务的传播性。
如果使用openSession,就要在dao层的方法中传递session,而这种做法是很糟糕的,首先增加了参数的个数,另外,方法是否需要事务,完全是可以当做一种独立的服务抽离出的。
因为currentSession是线程级别的,所以,只要业务逻辑方法在同一个线程中,就不会担心上面的问题。这也是currentSession的一个优越处之一。
使用currentSession:
1.在配置文件中将线程配置成Thread级别的。
- <SPAN style="FONT-SIZE: 18px"><propertynamepropertyname="hibernate.current_session_context_class">thread</property></SPAN>
<propertyname="hibernate.current_session_context_class">thread</property>
2.调用sessionFactory的getCurrentSession方法:
- <SPAN style="FONT-SIZE: 18px">publicvoid addUser(User user) {
- Session session = null;
- try {
- session =HibernateUtils.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- session.save(user);
- Loglog = new Log();
- log.setType("操作日志");
- log.setTime(new Date());
- log.setDetail("XXX");
- LogManager logManager = newLogManagerImpl();
- logManager.addLog(log);
- session.getTransaction().commit();
- }catch(Exception e) {
- e.printStackTrace();
- session.getTransaction().rollback();
- }
- }</SPAN>
publicvoid addUser(User user) {Session session = null;try {session =HibernateUtils.getSessionFactory().getCurrentSession();session.beginTransaction(); session.save(user); Loglog = new Log();log.setType("操作日志");log.setTime(new Date());log.setDetail("XXX"); LogManager logManager = newLogManagerImpl();logManager.addLog(log); session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback(); }}
使用openSession:
- <SPAN style="FONT-SIZE: 18px">public void addUser(User user) {
- Sessionsession = null;
- try{
- session= HibernateUtils.getSession();
- session.beginTransaction();
- // 若干操作…………
- session.getTransaction().commit();
- }catch(Exceptione) {
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
- </SPAN>
public void addUser(User user) {Sessionsession = null;try{session= HibernateUtils.getSession();session.beginTransaction();// 若干操作………… session.getTransaction().commit();}catch(Exceptione) {e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}
使用HibernateDaoSupport声明式事务:
Spring与Hibernate的集成使用最多的是HibernateDaoSupport,它对session的获取以及事务做了进一步的封装,只需要关注dao的实现,而不用担心某个地方的事务是否关闭。
- <SPAN style="FONT-SIZE: 18px">this.getHibernateTemplate().save(user);</SPAN>
this.getHibernateTemplate().save(user);
关于异常与事务回滚:
Spring在遇到运行期异常(继承了RuntimeException)的时候才会回滚,如果是Exception(如用户输入密码错误)抛出就好,事务会继续往下进行。
Spring对异常的处理的灵活性还是比较高的,可以配置遇到某个Exception进行回滚,某个RuntimeException不回滚,但是对于EJB就没有这么灵活了,EJB相当于是固定的套餐。
不会回滚:
- public void addUser(User user)
- throws Exception {
- this.getHibernateTemplate().save(user);
- //若干操作……
- throw new Exception();
- }
public void addUser(User user)throws Exception {this.getHibernateTemplate().save(user);//若干操作…… throw new Exception();}
回滚:
- public void addUser(User user) {
- this.getHibernateTemplate().save(user);
- //若干操作……
- throw new RuntimeException();
- }
public void addUser(User user) {this.getHibernateTemplate().save(user); //若干操作…… throw new RuntimeException();}
Spring与Hibernate的集成,使用HibernateDaoSupport的配置:
在ssh框架应用中,Spring与Hibernate的事务集成基本上是比较固定的,我们把事务的集成单独配置到applicationContext-common.xml中:
- <SPAN style="FONT-SIZE: 18px"><?xml version="1.0"encoding="UTF-8"?>
- <beansxmlnsbeansxmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
- <!--配置SessionFactory -->
- <beanidbeanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
- <propertynamepropertyname="configLocation">
- <value>classpath:hibernate.cfg.xml</value>
- </property>
- </bean>
- <!--配置事务管理器 -->
- <beanidbeanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <propertynamepropertyname="sessionFactory">
- <refbeanrefbean="sessionFactory"/>
- </property>
- </bean>
- <!--那些类那些方法使用事务 -->
- <aop:config>
- <aop:pointcutidaop:pointcutid="allManagerMethod" expression="execution(*com.bjpowernode.usermgr.manager.*.*(..))"/>
- <aop:advisorpointcut-refaop:advisorpointcut-ref="allManagerMethod" advice-ref="txAdvice"/>
- </aop:config>
- <!--事务的传播特性 -->
- <tx:adviceidtx:adviceid="txAdvice" transaction-manager="transactionManager">
- <tx:attributes>
- <tx:methodnametx:methodname="add*" propagation="REQUIRED"/>
- <tx:methodnametx:methodname="del*" propagation="REQUIRED"/>
- <tx:methodnametx:methodname="modify*" propagation="REQUIRED"/>
- <tx:methodnametx:methodname="*" propagation="REQUIRED"read-only="true"/>
- </tx:attributes>
- </tx:advice>
- </beans></SPAN>
<?xml version="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd"><!--配置SessionFactory --><beanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation"><value>classpath:hibernate.cfg.xml</value></property></bean><!--配置事务管理器 --><beanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory"><refbean="sessionFactory"/> </property></bean><!--那些类那些方法使用事务 --><aop:config><aop:pointcutid="allManagerMethod" expression="execution(*com.bjpowernode.usermgr.manager.*.*(..))"/><aop:advisorpointcut-ref="allManagerMethod" advice-ref="txAdvice"/></aop:config><!--事务的传播特性 --> <tx:adviceid="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:methodname="add*" propagation="REQUIRED"/><tx:methodname="del*" propagation="REQUIRED"/><tx:methodname="modify*" propagation="REQUIRED"/><tx:methodname="*" propagation="REQUIRED"read-only="true"/></tx:attributes></tx:advice></beans>
因为在hibernate.cfg.xml中添加了如下配置,所以,在tomcat等容器启动的时候,会自动将相应的bean对象创建。
- <SPAN style="FONT-SIZE: 18px"> <propertynamepropertyname="hibernate.hbm2ddl.auto">update</property></SPAN>
<propertyname="hibernate.hbm2ddl.auto">update</property>
applicationContext-beans.xml:
通常将业务逻辑对实现类的引用单独的xml文件中,同时,在实现类中不能忽略sessionFactory工厂的注入。
- <SPAN style="FONT-SIZE: 18px"><?xml version="1.0"encoding="UTF-8"?>
- <beansxmlnsbeansxmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd
- http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
- <beanidbeanid="userManager" class="com.bjpowernode.usermgr.manager.UserManagerImpl">
- <propertynamepropertyname="sessionFactory" ref="sessionFactory"/>
- <propertynamepropertyname="logManager" ref="logManager"/>
- </bean>
- <beanidbeanid="logManager"class="com.bjpowernode.usermgr.manager.LogManagerImpl">
- <propertynamepropertyname="sessionFactory" ref="sessionFactory"/>
- </bean>
- </beans></SPAN>
<?xml version="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd"><beanid="userManager" class="com.bjpowernode.usermgr.manager.UserManagerImpl"><propertyname="sessionFactory" ref="sessionFactory"/><propertyname="logManager" ref="logManager"/></bean><beanid="logManager"class="com.bjpowernode.usermgr.manager.LogManagerImpl"><propertyname="sessionFactory" ref="sessionFactory"/></bean></beans>
事务传播特性:
为了保证调用的业务逻辑方法都使用同一个事务,通常都使用REQUIRED这个级别,它表示:如果上一个方法中有事务,就直接使用,如果没有,就创建一个事务,这样,一旦事务创建了后,后续调用的方法就不会再创建。
其他的事务传播特性见下表:
Spring事务的隔离级别:
1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
另外四个与JDBC的隔离级别相对应。
2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
事务隔离级别主要应用在对大数据的处理方面,与锁的机制是密不可分的,这里不赘述。