Spring:九、声明式事务

12、声明式事务

AOP 在 Spring 框架中的作用:

  • 提供声明式企业服务,最重要的是声明式事务管理
  • 允许用户自定义切面,使用 AOP 来补充 OOP

12.1、事务

  1. 事务中包含的一组业务,要么都成功,要么都失败
  2. 确保数据的完整性和一致性
  3. ACID 原则
    • 原子性(Atom):要么都成功,要么都失败
    • 一致性(Consistency):要么都被提交,要么都不被提交;
    • 隔离性(Isolation):多个业务操作同一个资源时,防止数据损坏(脏读)
    • 持久性(Durability):事务一旦提交,结果就被持久化到存储器中。

12.2、搭建环境

整合 MyBatis ,再进行以下测试。

1、Mapper

增删改操作需要提交事务,因此在 Mapper 中添加一个 insert 方法和 一个 delete 方法来测试。

UserMapper

/**
 * 添加用户
 * @param user 待添加用户
 * @return 受影响行数
 */
int insertUser(User user);

/**
 * 删除用户
 *
 * @param id 用户ID
 * @return 受影响行数
 */
int deleteUser(long id);

UserMapper.xml

<insert id="insertUser" parameterType="user">
    insert into mybatis.user(id, name, password)
    values (#{id}, #{name}, #{password});
</insert>

<delete id="deleteUser" parameterType="_long">
    delete
    from mybatis.user
    where id = #{id}
</delete>

UserMapperImpl

public class UserMapperImpl implements UserMapper {
    /**
     * SqlSessionTemplate:获取Mapper接口,执行方法
     */
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public int insertUser(User user) {
        return sqlSession.getMapper(UserMapper.class).insertUser(user);
    }

    @Override
    public int deleteUser(long id) {
        return sqlSession.getMapper(UserMapper.class).deleteUser(id);
    }
}

2、测试

分别对两个方法进行测试

测试 insert 方法 :增加两个用户

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    int i = -1;
    i = userMapper.insertUser(new User(10001, "u10001", "123456"));
    if (i > 0) {
        System.out.println("插入成功");
    }

    i = userMapper.insertUser(new User(10002, "u10002", "123456"));
    if (i > 0) {
        System.out.println("插入成功");
    }
}

结果:插入成功。

Spring:九、声明式事务

测试 delete 方法 : 删除用户

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    // List<User> users = userMapper.listUsers();
    //
    // for (User user : users) {
    //     System.out.println(user);
    // }

    int i = -1;

    i = userMapper.deleteUser(10001);
    if (i > 0) {
        System.out.println("删除成功");
    }
}

结果:删除成功

Spring:九、声明式事务

测试事务

UserMapper.xml

delete 语句修改为错误的 SQL 语句(将 delete 关键字改成 deletes),使其无法正确执行。

<delete id="deleteUser" parameterType="_long">
    deletes
    from mybatis.user
    where id = #{id}
</delete>

UserMapperImpl

listUsers 方法作为事务来测试,调用 insertdelete 方法,查看结果。

@Override
public List<User> listUsers() {
    // 增加用户101
    insertUser(new User(101,"u101","123456"));
    // 删除用户10002
    deleteUser(10002);
    return sqlSession.getMapper(UserMapper.class).listUsers();
}

JUnit

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    List<User> users = userMapper.listUsers();

}

结果

  1. 删除语句报错

    Spring:九、声明式事务

  2. 用户101成功插入,用户10002未被删除

    Spring:九、声明式事务

我们想要的结果是:要么都成功,要么都失败。

即要么用户101 插入成功且用户10002被删除,要么用户101没插入且用户10002没被删除。

因此需要开启事务,来达到预期结果。

12.3、Spring声明式事务

12.3.1、导入配置

mybatis-config

使用 AOP 实现 Spring 声明式事务,因此需要导入 AOP 和 事务的配置信息。

xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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

12.3.2、设置事务

mybatis-config

  1. 开启事务管理
    • 使用的 dataSource 数据源,必须与 sqlSessionFactory 是同一个。
  2. 配置事务通知
  3. tx-mehod:要开启事务的方法
    • name:方法名,可以使用通配符*
    • propagation:事务传播机制;
      • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  4. 配置AOP
<!-- 开启事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg ref="dataSource"/>
</bean>

<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="listUsers" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!-- 配置AOP:切入事务 -->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* indi.jaywee.mapper.UserMapperImpl.* (..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

12.3.3、测试

UserMapperImpl

测试 insert 用户103、delete 用户 10002,查看结果

@Override
public List<User> listUsers() {
    // 增加用户101
    insertUser(new User(103,"u103","123456"));
    // 删除用户10002
    deleteUser(10002);
    return sqlSession.getMapper(UserMapper.class).listUsers();
}

Junit

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);

    List<User> users = userMapper.listUsers();

}

结果

  1. 删除语句报错

    Spring:九、声明式事务

  2. 用户103未被插入,用户10002未被删除:即事务成功开启

    Spring:九、声明式事务

上一篇:MyBatis配置动态SQL语句基础


下一篇:MyBatis笔记(一)------从0到1的MyBatis程序