Spring事务管理
1、事务回顾,什么是事务,事务的四个特性:
(1)事务是指这样一组操作,这组操作要么全做,要么全不做
(2)四个特性
原子性:是指事务中的操作是不可分割的,这些操作要么全部完成,要么全部不完成。
隔离性:是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
一致性:是指事务前后数据的完整性要保持一致
持久性:是指一个事务一旦提交,它对数据库中数据的改变就将是永久性的,即使数据库发生故障也不应该对其有任何影响。
2、什么是脏读、不可重复读,幻读
脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了,在后来的查询中,第一个事务就会发现某些原来不存在的记录
3、事务的传播行为:
web层 业务层 持久层
一、编程式事务处理
在applicationContext.xml里面配置
<?xml version="1.0" encoding="UTF-8"?> <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:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <context:component-scan base-package="com.zlc" /> <mvc:annotation-driven /> <bean id="viewresolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"></property> <property name="suffix" value=".jsp"></property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactory" ref="ssf"></property> <!-- 扫描com.zlc.dao下所有接口,批量生成实现实例,并交于spring管理 --> <property name="basePackage" value="com.zlc.dao"></property> </bean> <!-- SqlSessionFactoryBean封装了创建SqlSessionFactory组件的过程 --> <bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入dataSource --> <property name="dataSource" ref="dataSource"></property> <!-- 注入Mapper.xml文件位置信息 --> <!-- 这样配置就会扫描com/zlc/mapper/下的所有xml文件 --> <property name="mapperLocations" value="classpath:com/zlc/mapper/*.xml"></property> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="#{dbParams.user}"></property> <property name="password" value="#{dbParams.password}"></property> <property name="driverClass" value="#{dbParams.driverClass}"></property> <property name="jdbcUrl" value="#{dbParams.jdbcUrl}"></property> </bean> <util:properties location="classpath:db.properties" id="dbParams"> </util:properties> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> </beans>
在Service层:
package com.zlc.serviceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import com.zlc.dao.AccountDao; import com.zlc.service.AccountService; @Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private TransactionTemplate transactionTemplate; public boolean transfer(final Integer in, final Integer out, final Double money) { transactionTemplate.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { accountDao.outMoney(out, money); int i = 1/0; accountDao.inMoney(in, money); return null; } }); return true; } }
其中粗体字中的内容即被包含为一个事务进行处理。要么全部成功,要么全部不成功,若出现异常则进行回滚。
二、声明式事务处理
(1)使用TransactionProxyFactoryBean进行声明式事务处理
<?xml version="1.0" encoding="UTF-8"?> <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:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <context:component-scan base-package="com.zlc" /> <mvc:annotation-driven /> <bean id="viewresolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"></property> <property name="suffix" value=".jsp"></property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactory" ref="ssf"></property> <!-- 扫描com.zlc.dao下所有接口,批量生成实现实例,并交于spring管理 --> <property name="basePackage" value="com.zlc.dao"></property> </bean> <!-- SqlSessionFactoryBean封装了创建SqlSessionFactory组件的过程 --> <bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入dataSource --> <property name="dataSource" ref="dataSource"></property> <!-- 注入Mapper.xml文件位置信息 --> <!-- 这样配置就会扫描com/zlc/mapper/下的所有xml文件 --> <property name="mapperLocations" value="classpath:com/zlc/mapper/*.xml"></property> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="#{dbParams.user}"></property> <property name="password" value="#{dbParams.password}"></property> <property name="driverClass" value="#{dbParams.driverClass}"></property> <property name="jdbcUrl" value="#{dbParams.jdbcUrl}"></property> </bean> <util:properties location="classpath:db.properties" id="dbParams"> </util:properties> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> <!-- 配置业务层的代理 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置目标对象 --> <property name="target" ref="accountService"></property> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager"></property> <property name="transactionAttributes"> <props> <!-- prop格式: key:匹配的方法名,可以使用通配符,如update* * PROPAGATION :事务的传播行为 * ISOLATION :事务的隔离级别 * readOnly :只读 * -Exception :发生哪些异常进行事务回滚 * +Exception :发生哪些异常不进行事务回滚 --> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
service层:
package com.zlc.serviceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import com.zlc.dao.AccountDao; import com.zlc.service.AccountService; @Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public boolean transfer(Integer in, Integer out, Double money) { accountDao.outMoney(out, money); int i = 1 / 0; accountDao.inMoney(in, money); return true; } }
Test:
package com.zlc.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zlc.dao.UserDao; import com.zlc.entity.User; import com.zlc.service.AccountService; public class UserTest { public static void main(String[] args){ String conf = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(conf); AccountService accountService = ac.getBean("accountServiceProxy",AccountService.class); accountService.transfer(1, 2, 100.0); } }
这里改为使用代理类进行访问。