(六)Spring-事务式声明
(六)Spring-事务式声明
一、回顾事务
1.1 事务特性:
- ACID
- 把一组业务当成一个业务来做,要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题。不能马虎!
- 确保完整性和一致性!
1.2 事务ACID原则:
- 原子性 A
- 一致性 C
- 隔离性 I
- 多个业务可能操作同一个资源,如一条记录,防止数据损坏即确保完整性和一致性。
- 持久性 D
二、spring中的事务管理
2.1 声明式事务:AOP
1 编写service层业务逻辑,包含事务
-
service层实现,通过容器注入属性mapper
-
在业务逻辑中使用mapper即dao层
-
编写事务,
doTx()方法先添加后修改user,中间制造错误,如下:
package com.happy.service.user.impl;
import com.happy.mapper.user.UserMapper;
import com.happy.pojo.user.User;
import com.happy.service.user.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userMapper")
UserMapper userMapper;
@Override
public List getUserList() {
List userList = userMapper.getUserList();
return userList;
}
@Override
public User getUserById(int id) {
User user = userMapper.getUserById(id);
return user;
}
@Override
public int addUser(User user) {
int result = userMapper.addUser(user);
return result;
}
@Override
public int deleteUser(User user) {
int result = userMapper.deleteUser(user);
return result;
}
@Override
public int updateUser(User user) {
int result = userMapper.updateUser(user);
return result;
}
@Override
public void doTx(){
List userList = userMapper.getUserList();
System.out.println(userList);
System.out.println("==========执行一组业务,先增后删==============");
User user = new User(521, "gao518", "1987518");
/*1 增加*/
System.out.println("1.增加用户");
userMapper.addUser(user);
/*制造报错*/
int error=1/0;
/*2 删除*/
User user2 = new User(520, "gao518518", null);
System.out.println("2.删除用户");
userMapper.updateUser(user2);
userList = userMapper.getUserList();
System.out.println(userList);
}
}
2 编写mapper接口和mapper.xml文件
package com.happy.mapper.user;
import com.happy.pojo.user.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List getUserList();
User getUserById(@Param("id") int id);
/*增*/
int addUser(User user);
/*删*/
int deleteUser(User user);
/*改*/
int updateUser(User user);
}
<?xml version="1.0" encoding="UTF-8" ?>
insert into user(`id`, `name`, `pwd`)
values (#{id}, #{name}, #{pwd})
delete from user where id=#{id};
update user
name=#{name},
pwd=#{pwd},
where id=#{id};
3 开启Spring的事务处理功能
在相关的spring xml中如spring-mybatis.xml,要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:
4 配置切面(通知)和切点
-
配置切面
- 结合aop实现事务的织入
- 配置事务的类即:定义事务切面和通知即事务增强方法
经过实测,方法名必须和严格一致或使用通配符,如method name="do*" propagation="REQUIRED"
- 配置事务的传播特性
-
配置切点
- 即配置通知的作用点
- 作用点一般定义在service层,请注意
- service层,作用在接口和实现类均可。即可以是com.happy.service.user.UserService.,也可以是com.happy.service.user.impl.UserServiceImpl.
<?xml version="1.0" encoding="UTF-8"?>
5 模拟controller调用service层方法,测试事务
- 注意:
必须从容器获取service层实例context.getBean("userService", UserService.class);,不能自己用构造器new
package com.happy.service.user;
import com.happy.pojo.user.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class UserServiceTest {
@Test
public void testGetUserList() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
UserService userService = context.getBean("userService", UserService.class);
List userList = userService.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void testGetUserById() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = userService.getUserById(3);
System.out.println(user);
}
@Test
public void testTransaction() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
UserService userService = context.getBean("userService", UserService.class);
//注意:必须是被容器管理的对象,执行相关方法,才能被AOP拦截。
userService.doTx();
}
}
2.2 编程式事务:需要在代码中,进行事务的管理。
public class UserService {
private final PlatformTransactionManager transactionManager;
public UserService(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void createUser() {
TransactionStatus txStatus =
transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
userMapper.insertUser(user);
} catch (Exception e) {
transactionManager.rollback(txStatus);
throw e;
}
transactionManager.commit(txStatus);
}
}
-
需要在原有业务逻辑上,通过手动代码实现事务,即try-catch+rollback
-
要改变代码,现在一般不用了
2.3 小结
思考:
为什么需要事务
- 如果不配置事务,可能存在数据提交不一致的情况。如银行转账绝对不允许。
- 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
- 事务在项目的开发中是否重要,涉及到数据的一致性和完整性问题,不容马虎。
- 工作的时候,都建议配置上声明式事务,都是死的。