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 编程式事务(代码管理)

需要改变原有代码(略)