Version: Next
声明式事务
1. 回顾事务
- 要么都成功,要么都失败
- 事务在项目开发中,十分重要,涉及到数据一致性问题,不能马虎
- 确保完整性和一致性
事物的ACID原则:【面试】 意思就是 要么都成功要么都失败
- 原子性 不可再分
- 一致性 事务前后数据的完整性必须保持一致
- 隔离性 操作之间互不影响,防止数据损坏
- 持久性 操作的结果是永久的
1.1 环境搭建
- 新建maven模块spring-11-transaction
- 建pojo
- 写接口
- mybatis和spring配置文件
- 整合Mybatis
- 测试
1.2 模拟事务
Mapper接口
//添加一个用户public int addUser(User user);//删除一个用户public int deleteUser(@Param("id") int id);Mapper.xml 为了模拟事务,故意把delete语句写错,把delete写成deletes
<mapper namespace="com.bsx.mapper.UserMapper"><select id="queryUser" resultType="com.bsx.pojo.User">SELECT * FROM USER;</select><insert id="addUser" parameterType="com.bsx.pojo.User">insert into user (id, name, password) values (#{id} , #{name} , #{password})</insert><delete id="deleteUser" >deletes from user where id = #{id}</delete></mapper>mapper实现类
public class UserMapperImpl implements UserMapper {private SqlSessionTemplate sqlSession;//用来让Spring注入public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}public List<User> queryUser() {User user = new User();user.setId(6);user.setName("第六个");user.setPassword("123455");UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.addUser(user);mapper.deleteUser(6);return mapper.queryUser();}public int addUser(User user) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.addUser(user);}public int deleteUser(int id) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.deleteUser(id);}}测试 报错,插入成功、删除失败,这是我们不希望看到的
2. Spring中的事务
2.1 声明式事务 (AOP)
无需改变原有代码
要开启Spring事务处理功能,需要在Spring配置文件中创建一个DataSourceTransactionManager
对象
<!-- 开启事务管理-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="datasource" />
</bean>
2.1.1 配置实现
配置声明式事务
<!-- 结合AOP实现事务的织入-->
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<!-- 给哪些方法配置事务-->
<!-- 配置事务的传播特性 propagation 默认值为REQUIRED-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务-->
<!-- 配置事务的传播特性 propagation 默认值为REQUIRED-->
<tx:attributes>
<tx:method name="*"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="query*" read-only="true"/>
</tx:attributes>
</tx:advice>
</tx:advice>
<!-- 配置事务切入 mapper包下所有类所有方法任意参数-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.bsx.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
测试
@Test
public void testMybatis() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> users = userMapper.queryUser();
for (User user : users) {
System.out.println(user);
}
}
2.1.2 注解实现
开启事务管理
- 开启事务注解
- 无序配置aop
<!-- 开启事务管理--><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><constructor-arg ref="datasource" /></bean><tx:annotation-driven transaction-manager="transactionManager"/>在Mapper实现类中使用
@Transactional
注解public class UserMapperImpl implements UserMapper {private SqlSessionTemplate sqlSession;//用来让Spring注入public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED,readOnly = false)public List<User> queryUser() {User user = new User();user.setId(6);user.setName("第五个");user.setPassword("123455");UserMapper mapper = sqlSession.getMapper(UserMapper.class);mapper.addUser(user);mapper.deleteUser(6);return mapper.queryUser();}public int addUser(User user) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.addUser(user);}public int deleteUser(int id) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.deleteUser(id);}}
2.1.3 七种Propagation事务属性
Spring中Propagation类的七种事务属性
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。常用
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常
- REQUIRED_NEW:新建事务,如果当前存在事务,把当前事务挂起
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
- NEVER:以非实物方式执行,如果当前存在事务,则抛出异常
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务
2.1.4 四种Isolation事务隔离属性
Spring中事务的隔离级别可以通过隔离属性指定
- DEFAULT:使用底层数据库默认隔离级别,大部分数据库,默认隔离级别为READ_COMMITED
- READ_COMMITED:只允许事务读取已经被其他事务提交的更改,可以避免藏独,但不可重复读和幻读问题仍然可能出现
- READ_UNCOMMITED:允许事务读取未被其他事务提交的更改。脏读,不可重复读,幻读都有可能出现
- REPEATABLE_READ:确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但是幻读的问题依然存在
- SERIALIZABLE:确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新,删除。所有的并发问题都能避免,但是性能比较低。
2.2 编程式事务(代码管理)
需要改变原有代码(略)