上一篇文章我们进行了基本的框架搭建,实现了登录和全查功能。这都属于查询操作,如果要进行增删改等需要对数据库修改的操作时就需要进行事务操作。
我们在执行增删改操作的时候在方法中使用session缓存手动开启事务,等执行完持久化操作之后在进行事务的提交。这就是编程式事务,但是每个方法都进行一个事务的开启,提交,回滚等操作的话太过麻烦。
我们现在是SSH框架的整合,由Spring对Hibernate和Struts2进行管理,Spring可以拥有全部的控制权,其中就包括事务,Spring的Aop提供了对事务的支持,只需要在Spring配置文件中声明事务就可以实现对事务的统一管理,并且不需要再在DAO层的方法中手动的开启事务,能简化对数据的持久化操作。这是声明式事务。
方法一:声明式事务的配置:
继续使用上一片文章中搭建项目,打开applicationContext.xml,声明式事务的配置就需要增加applicationContext.xml的命名空间,tx和aop的命名空间,下面是增加命名空间后的头部信息
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
首先需要配置Hibernate事务管理器bean,并注入sessionFactory属性
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
这样就可以使用Spring管理Hibernate的事务,然后配置事务增强,就是配置事务的规则,指定那些方法执行事务和执行事务的规则
<!--
<tx:advice>定义事务通知,用于指定事务属性,其中“transaction-manager”属性指定事务管理器,并通过<tx:attributes>指定具体需要拦截的方法
<tx:method>拦截方法,其中参数有:
name:方法名称,将匹配的方法注入事务管理,可用通配符
propagation:事务传播行为,
isolation:事务隔离级别定义;默认为“DEFAULT”
timeout:事务超时时间设置,单位为秒,默认-1,表示事务超时将依赖于底层事务系统;
read-only:事务只读设置,默认为false,表示不是只读;
rollback-for:需要触发回滚的异常定义,可定义多个,以“,”分割,默认任何RuntimeException都将导致事务回滚,而任何Checked Exception将不导致事务回滚;
no-rollback-for:不被触发进行回滚的 Exception(s);可定义多个,以“,”分割;
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 事务规则,指定那些方法执行事务和事务的规则 -->
<tx:attributes>
<!-- query* 指以query开头的方法以事务的方式执行 -->
<tx:method name="query*" read-only="true" propagation="REQUIRED"/>
<tx:method name="*Update" read-only="false" propagation="REQUIRES_NEW" />
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
然后定义事务的切入点,事务的切入点使用execution表达式进行描述,表达式描述事务的切入点方法所在类的全路径,因为service层有可能执行多个dao层的方法,所以我们这里定义service层的所有方法
<aop:config proxy-target-class="true">
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* org.hua.service.*.*(..))" id="pointcut"/>
<!-- 把定义的事务切入点和通知连接起来, -->
<aop:advisor advice-ref="txAdvice" pointcut="pointcut"/>
</aop:config>
注意如果事务切入的类实现了至少一个接口那么就会出现
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type
或者Java.lang.ClassCastException: 异常,有两个解决办法
- 1:就是设置proxy-target-class="true"这个属性,proxy-target-class="true"这个属性一定要设置为true(默认false),这个属性是指定基于接口还是类的代理被创建,如果不设置默认为false
- 2:Spring Ioc容器中放入的是哪个接口的实现类就声明那个接口接收
execution表达式常见举例
自此就完成Spring的声明式事务的配置
方法二:使用@Transactional实现注解事务管理
spring配置文件头部信息跟上边一样
同样配置事务管理器,然后开启注解事务
<!-- hibernate的事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
然后在需要开启事务的类或方法上边直接加上@Transactional即可
事务规则可以在注解里面进行配置,具体规则参照上面
@Transactional(readOnly=true,rollbackFor=Exception.class,propagation=Propagation.REQUIRED)
public void empDel(int id) {
edi.empDel(id);
}
Spring提供的@Transaction注解事务管理,内部同样是利用环绕通知TransactionInterceptor实现事务的开启及关闭。
使用@Transactional注意点:
- 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口;
- 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题;而使用使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”