Spring中进行事务管理的通常方式是利用AOP(面向切片编程)的方式,为普通java类封装事务控制,它是通过动态代理实现的,由于接口是延迟实例化的,spring在这段时间内通过拦截器,加载事务切片。动态代理的一个重要特征是,它是针对接口的,所以我们的dao和service要通过动态代理来让spring接管事务,就必须在dao或者service前面抽象出一个接口。
1、声明式事务配置
<!--1 数据源配置--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://127.0.0.1:3306/DataBaseName?useUnicode=true&characterEncoding=UTF-8</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>root</value> </property> </bean> <!--2 sessionFactory源配置--> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref local="dataSource"/> </property> <property name="mappingResources"> <list> <value>com/net/dao/hbm/****.hbm.xml</value> <value>com/net/dao/hbm/****.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> <!--3 定义事务管理器--> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory"/> </property> </bean> <!--4 给dao注入sessionFactory--> <bean id="exampleDAO" class="com.net.dao.impl.ExampleDAO"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean> <!--5 给dao注入事务管理器,并指定其对应方法的事务传播特性--> <bean id="proxyExampleDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="target"> <ref local="exampleDAO" /> </property> <property name="transactionAttributes"> <props> <prop key="add*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="delete*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!--6 给service层配置事务--> 同4、5,也是给相应的service bean注入sessionFactory,然后再为其注入事务管理器,并制定对应方法的事务传播特性。
注:1)、在界定事务边界时,我们可以给dao层界定事务边界,也可以给service层界定边界,但个人建议直接给service层界定事务边界,而不需要给dao层界定事务边界,我们操作数据库都通过service层来操作dao层,而后通过dao层与数据库交互,更好的体现了MVC分层思想。
2)、当一个事务内出现异常时,spring默认的事务回滚只是对RuntimeException(包括继承RuntimeException的子类)进行回滚操作,对于checked exception则不进行回滚,事务就会失败,要想使事务起作用,必须在定义事务传播特性时加上其checked exception,也可以通过指定其抛出异常为RuntimeException来解决,具体实现方法参考:谁让Spring的事务控制不起作用了
3)、若按以下方法对service层处理,则不需要在spring配置文件中定义事务传播特性时指明checked exception。
2、编写业务逻辑方法
public void deleteEx(Integer exId) throws MyException{ try { updateEx(); addEx(); deleteEx(); }catch (Exception e) { e.printStackTrace(); throw new MyException(e.getMessage()); } }
注:这里的MyException是我自己定义的Exception,继承自RuntimeException,这样如果业务逻辑出现异常,事务也不会因为对异常的处理而终止。而是会随着异常抛出给显示层后进行事务的回滚操作。以下是MyException的实现。
public class MyException extends RuntimeException { private static final long serialVersionUID = 1L; private int errorCode; public MyException() { errorCode = 0; } public MyException (String errorMsg) { super(errorMsg); errorCode = 0; } public MyException (int errorCode, String errorMsg) { super(errorMsg); this.errorCode = errorCode; } public int getErrorCode() { return this.errorCode; } public void setErrorCode(int errorCode) { this.errorCode = errorCode; }
3、编写持久层方法
同业务逻辑层。
重点:(1)、声明式事务默认的回滚是RuntimeException(包括继承RuntimeException的子类),checked 异常不回滚。
(2)、在编写业务逻辑方法和持久层时,最好将异常一直往上抛出,在表现层处理(struts),要是在回到表现层之前处理后,也要将异常重新抛出,否则事务将会中断,如第二步所示。即web成功捕获异常后spring事务管理才会成功!(个人见解)
(3)、spring的事务需要设置到业务方法上(事务边界定义到Facade类上),最好不要添加到Dao层。(个人见解)
相关属性:Spring事务的隔离级别 :
(1)、ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应
(2)、ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。
(3)、ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
(4)、ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
(5)、ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
Spring事务事务的几种传播特性:
(1)、PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启。
(2)、PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
(3)、PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
(4)、 PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
(5)、PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
(6)、PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常。
(7)、PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。