12、声明式事务
AOP 在 Spring 框架中的作用:
- 提供声明式企业服务,最重要的是声明式事务管理;
- 允许用户自定义切面,使用 AOP 来补充 OOP 。
12.1、事务
- 事务中包含的一组业务,要么都成功,要么都失败;
- 确保数据的完整性和一致性;
-
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("插入成功");
}
}
结果:插入成功。
测试 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("删除成功");
}
}
结果:删除成功
测试事务
UserMapper.xml
将 delete 语句修改为错误的 SQL 语句(将 delete 关键字改成 deletes),使其无法正确执行。
<delete id="deleteUser" parameterType="_long">
deletes
from mybatis.user
where id = #{id}
</delete>
UserMapperImpl
将 listUsers 方法作为事务来测试,调用 insert 和 delete 方法,查看结果。
@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();
}
结果:
-
删除语句报错
-
用户101成功插入,用户10002未被删除
我们想要的结果是:要么都成功,要么都失败。
即要么用户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
- 开启事务管理
- 使用的 dataSource 数据源,必须与 sqlSessionFactory 是同一个。
- 配置事务通知
- tx-mehod:要开启事务的方法
-
name:方法名,可以使用通配符
*
-
propagation:事务传播机制;
- REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-
name:方法名,可以使用通配符
- 配置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();
}
结果:
-
删除语句报错
-
用户103未被插入,用户10002未被删除:即事务成功开启