文章目录
- 一、声明式事务
- 1.1 什么是事务
- 1.2 事务的应用场景
- 1.3 事务的特性(ACID)
- 1.4 未使用事务的代码示例
- 1.5 配置 Spring 声明式事务学习总结
一、声明式事务
1.1 什么是事务
-
把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
1.2 事务的应用场景
-
在我们日常开发中,具体的业务大多数都是操作数据库的增删改查,其中包含了很多原子性的数据库操作。当同一个业务中出现多个原子性的数据库操作时,为了数据的安全性,避免数据不同步的状况发生就需要用到事务。
-
举个栗子,银行转账
-
两个人来到银行进行转账的操作,A 需要转给 B 1000块钱,也就是 A 的钱减少一千,B 的钱增加一千。但是如果在转账途中,出现银行系统崩溃或者网络故障等状况导致 A 的钱确实是少了一千,但是 B 的钱并没有增加,这显然是不对的。这时候事务的功效就显示出来了,两个操作要么就都成功,要么直接事务回滚,让操作都失败,以此来保证数据的安全性和一致性。
-
-
1.3 事务的特性(ACID)
-
原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
-
一致性(Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
-
隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
-
持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!
1.4 未使用事务的代码示例
-
项目背景 Spring + Mybatis ,将第八章的项目原封不动的粘过来,修改 mapper.xml ,将删除 SQL 改成错误的写法其他内容不变
-
修改测试类(新增一条数据,在将这条数据删除)
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-config.xml") public class Test_SpringMyBatis { @Autowired private UsersMapper userMapper; @Test public void testFindUserList(){ Users users = new Users(); users.setId("4"); users.setName("姚青"); users.setPwd("123456"); int i = userMapper.saveUser(users); int i3 = userMapper.deleteUsers(users); } }
-
执行结果,可以看出执行到 delete 方法时候报错了,但是第一条新增添加成功了,这显然不是很合理
1.5 配置 Spring 声明式事务学习总结
-
上面的代码执行出来的结果明显是不符合真实情况的,所有这里需要配置 Spring 的声明式事务
-
在 Spring 配置文件中新增配置
-
使用Spring管理事务,需要导入头文件的约束 : tx
xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
-
配置 Spring 声明式事务
<!-- 配置 Spring 声明式事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource"/> </bean>
-
配置好声明式事务之后,结合 Spring aop 实现事务的织入(这里需要引入 tx (事务))
<!-- 结合 Spring aop 实现事务的织入 --> <!-- 第一步:配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给那些方法配置事务,可以是具体的方法名,也可以是*,*代表所有方法 --> <!--配置事务的传播特性: new propagation--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 第二步:配置事务切入 --> <aop:config> <!-- 指定切入点为 mapper 包下所有接口中的所有方法 --> <aop:pointcut id="txPointCut" expression="execution(* com.sys.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
-
-
Spring 完整配置
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 加载数据库连接信息的属性文件 --> <context:property-placeholder location="classpath:jdbc-config.properties"/> <!-- 配置Druid数据源的Bean --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置SessionFactory的Bean --> <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 指定MyBatis配置文件的位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 配置 xml 的映射 --> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <!-- 配置mapper接口的扫描器,将Mapper接口的实现类自动注入到IoC容器中 实现类Bean的名称默认为接口类名的首字母小写 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- basePackage属性指定自动扫描mapper接口所在的包 --> <property name="basePackage" value="com.sys.mapper"/> </bean> <!-- 配置 Spring 声明式事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource"/> </bean> <!-- 结合 Spring aop 实现事务的织入 --> <!-- 第一步:配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给那些方法配置事务,可以是具体的方法名,也可以是*,*代表所有方法 --> <!--配置事务的传播特性: new propagation--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 第二步:配置事务切入 --> <aop:config> <!-- 指定切入点为 mapper 包下所有接口中的所有方法 --> <aop:pointcut id="txPointCut" expression="execution(* com.sys.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>
-
-
其他代码不变
- 测试结果:执行到 delete 方法时,依然报错,但是数据库没有新增数据,事务回滚成功。