Spring手动开启事务

开发工具与关键技术: Java
作者:肖广斌
撰写时间:2021年6月6日

事务是什么呐?
事务(Transaction),一般是指要做的或所做的事情。事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。事务的正确执行使得数据库从一种状态转换成另一种状态。

为什么要用事务?
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
举一个简单点的例子:就是支付宝转账业务,A账号要给B账号转200块,A账号少200,B账号要加200,假如转账过程中网络出现错误,A账号少了200块,而B账号没有加上200块,那这个业务就是操作失败,那就得控制它,总不能B账号没有收到A账号转的帐,A账号的钱还少了吧。A账号转帐业务撤销。这才能保证业务的正确性。将A账号资金减少和B账号资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。简单的说就是转账要么一起成功,要么一起失败。

事务的4个特性(ACID):
1、 原子性(atomicity):表示事务执行过程中的任何失败都将导致事务所做的任何修改失效,就是要么全部执行,要么全部不执行。
2、 一致性(consistency):表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
3、 隔离性(isolation):一个事务的执行不能被其他事务所影响。
4、 持久性(durability):表示已提交的数据在事务执行失败时,数据的状态都应该正确。

Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

事务并发处理可能引起的问题:
1、脏读(dirty read):一个事务读取了另一个事务尚未提交的数据。
2、不可重复读(non-repeatable read):一个事务的操作导致另一个事务前后两次读取到不同的数据。
3、幻读(phantom read):一个事务的操作导致另一个事务前后两次查询的结果数据量不同。
例子1:A、B事务并发执行时,A事务修改后,B事务读取到了A事务未提交的数据,此时A数据已经rollback回滚,则B事务读取到了A事务无效的“脏”数据。
例子2:当A事务读取数据后,B事务修改了A事务读取到的数据,这时A事务再去读取该数据,发现两次数据不一样。
例子3:当A事务查询数据后,B事务新增或删除了一条满足A事务查询的数据,此时A事务再去查询数据,发现查询到之前不存在的数据记录,或者之前的数据记录不存在了。

事务的七种传播行为:
1、PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的事务传播行为。
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,延缓当前事务。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务暂停。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。

事务的隔离级别:
1、ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
2、ISOLATION_READ_UNCOMMITTED(读取未提交内容):这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3、ISOLATION_READ_COMMITTED(读取提交内容):保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。
4、ISOLATION_REPEATABLE_READ(可重读):这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读。
5、ISOLATION_SERIALIZABLE(可串行化):这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

这里用到的是JDBC事务,在项目中我们使用事务之前,需要配置一些必需的配置文件,这是在spring-mybatis.xml里配置的,还有一种使用注解事务的配置,看个人所需,这里就不配置了

<!-- 事务管理 -->
<bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

在控制器的方法中开启事务

@ResponseBody
@RequestMapping(value = "/addStudents", produces = "application/json; charset=utf-8")
public JsonReturn addStudents(Students students,HttpServletRequest request)throws Exception{
    JsonReturn jsonReturn = new JsonReturn();
    AccountantRecord accountantRecord = new AccountantRecord();
    //获取Spring容器的对象
    WebApplicationContext contextLoader = ContextLoader.getCurrentWebApplicationContext();
    //设置属性的默认属性
    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
    //设置事务的传播行为,此处是设置为开启一个新事务
    definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    //设置事务的隔离级别,此处是读已经提交
    definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    //从spring容器对象中获取DataSourceTransactionManager,这个根据配置文件中配置的id名(transactionManager)
    DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) contextLoader.getBean("transactionManager");
    //获取事务状态对象
    TransactionStatus transactionStatus = (TransactionStatus) transactionManager.getTransaction(definition);
    try {
		//要执行的方法
        int intS = StudentsMapper.addStudents(students);
        if (intS>0){
    		StudentsMapper.addAccountantRecord(accountantRecord);//新增第二张表
		 }	
        //提交事务
        transactionManager.commit(transactionStatus);
    }catch (Exception e){
		//回滚事务
        transactionManager.rollback(transactionStatus);
        jsonReturn.setMsg("操作失败");  
    }
    return jsonReturn;
}

如果开启事务之后,同时新增多表发生异常,就会回滚事务,如下图:
在这里插入图片描述

以上为个人理解+结合资料所整理,如有错误,欢迎指正!!!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring中,我们可以通过编程的方式(即手动)来开启事务。具体步骤如下: 1. 在Spring配置文件中开启事务管理器: ``` <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> ``` 2. 在需要开启事务的方法上添加@Transactional注解: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Transactional public void transferMoney(int fromUserId, int toUserId, double money) { User fromUser = userDao.getUserById(fromUserId); User toUser = userDao.getUserById(toUserId); fromUser.setBalance(fromUser.getBalance() - money); toUser.setBalance(toUser.getBalance() + money); userDao.updateUser(fromUser); userDao.updateUser(toUser); } } ``` 在上面的例子中,我们在transferMoney方法上添加了@Transactional注解,这样Spring就会自动开启事务,并在方法执行完毕后自动提交或回滚事务。 需要注意的是,如果我们在一个类中调用了另一个类中被@Transactional注解的方法,那么事务并不会自动传播。因此,需要在调用方的方法上也添加@Transactional注解,并设置propagation属性为REQUIRED。例如: ``` @Service public class OrderServiceImpl implements OrderService { @Autowired private UserService userService; @Transactional(propagation = Propagation.REQUIRED) public void placeOrder(int userId, double amount) { // 生成订单,并扣除用户账户余额 userService.transferMoney(userId, 1, amount); // ... } } ``` 在上面的例子中,我们在placeOrder方法上也添加了@Transactional注解,并设置propagation属性为REQUIRED,这样事务就能够正确传播。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值