当前位置: 代码迷 >> 综合 >> spring有声明式事务和编程式事务,本篇看一下springboot下的@Transactional
  详细解决方案

spring有声明式事务和编程式事务,本篇看一下springboot下的@Transactional

热度:40   发布时间:2023-12-15 18:01:57.0

1. 前言

springboot下的@Transactional

2.编码
我们这里搞一个实例,转账实例,A用户转账给B用户xx元

Account类

package com.jd.entity;?import javax.persistence.*;@Entity@Table(name="t_account")public class Account {/*** id 编号*/@Id@GeneratedValueprivate Integer id;?/*** 用户名*/@Column(length=50)private String userName;?/*** 余额*/private float balance;?public Integer getId() {return id;}?public void setId(Integer id) {this.id = id;}?public String getUserName() {return userName;}?public void setUserName(String userName) {this.userName = userName;}?public float getBalance() {return balance;}?public void setBalance(float balance) {this.balance = balance;}}

1.启动启动类,会自动创建表t_account,我们插入两条数据(这里我练习还是使用之前 db_book 库,可以自己切换其他库)

insert into t_account values(1,700,'zhangsan');
insert into t_account values(2,300,'lisi');

代码请建立对应的包,可以参考我建立的包

2.新建AccountDao接口

package com.jd.dao;
?
import com.jd.entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
?
/*** Created by ${HeJD} on 2018/6/27.*/
public interface AccountDao extends JpaRepository<Account, Integer> {
}


3.AccountService接口

 

package com.jd.service;
?
/*** Created by ${HeJD} on 2018/6/27.*/
public interface AccountService {/*** 转账* @param fromUserId* @param toUserId* @param account*/public void transferAccounts(int fromUserId,int toUserId,float account);
}


4.AccountServiceImpl接口实现类

 

package com.jd.service.serviceImp;
?
import com.jd.dao.AccountDao;
import com.jd.entity.Account;
import org.springframework.stereotype.Service;
import com.jd.service.AccountService;
import javax.annotation.Resource;
?
/*** Created by ${HeJD} on 2018/6/27.*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
?@Resourceprivate AccountDao accountDao;
?public void transferAccounts(int fromUserId,int toUserId,float account){
?Account fromUserAccount=accountDao.getOne(fromUserId);fromUserAccount.setBalance(fromUserAccount.getBalance()-account);accountDao.save(fromUserAccount); // fromUser扣钱
?Account toUserAccount=accountDao.getOne(toUserId);toUserAccount.setBalance(toUserAccount.getBalance()+account);//假设转账的时候假如出现异常,业务类或业务方法中没有使用@Transactional控制事务,则会出现钱转出了,收钱人没有收到的情况// int zero = 1/0;  这里我们先把这个异常注释掉,稍后我们再打开accountDao.save(toUserAccount); // toUser加钱}
}


5.AccountController类

package com.jd.controller;
?
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.jd.service.AccountService;
?
import javax.annotation.Resource;
?
/*** Created by ${HeJD} on 2018/6/27.*/
?
@RestController
@RequestMapping("/account")
public class AccountController {
?@Resourceprivate AccountService accountService;
?@RequestMapping("/transfer")public String transferAccounts(){try{//1号zhsnasan  给2号lisi 转账200元accountService.transferAccounts(1, 2, 200);return "ok";}catch(Exception e){return "no";}}
}

6.我们执行启动类

浏览器输入:http://localhost:8080/account/transfer

 

运行OK,没有事务控制的时候,没有异常的时候,转出的前与入账的钱是一致的,没有出现丢失的情况。

OK 我们先把数据恢复到700 300

现在我们把service层方法改下,抛出一个异常

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.transaction.Transactional;
?
@Service("accountService")
public class AccountServiceImpl implements AccountService {
?@Resourceprivate AccountDao accountDao;
?/*** 转账** @param fromUserId* @param toUserId* @param account*/@Override
?public void transferAccounts(int fromUserId, int toUserId, float account) {Account fromUserAccount=accountDao.getOne(fromUserId);fromUserAccount.setBalance(fromUserAccount.getBalance()-account);accountDao.save(fromUserAccount); // fromUser扣钱
?Account toUserAccount=accountDao.getOne(toUserId);toUserAccount.setBalance(toUserAccount.getBalance()+account);
?//假设转账的时候假如出现异常,业务类或业务方法中没有使用@Transactional控制事务,则会出现钱转出了,收钱人没有收到的情况int zero = 1/0;   //打开这个异常accountDao.save(toUserAccount); // toUser加钱}
}

这时候 扣钱dao能执行成功 加钱操作执行不了了 因为前面会报错。

我们重启启动类

浏览器输入:http://localhost:8080/account/transfer

 

运行NO

这时候 钱扣了 但是 没加钱 导致了数据不一致性!!!!

3.事务的使用
这时候 我们需要用上事务

在service类或方法上加上@Transactional即可,无需xml文件的配置,这也是springboot的优点

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

 

package com.jd.service.serviceImp;
?
import com.jd.dao.AccountDao;
import com.jd.entity.Account;
import org.springframework.stereotype.Service;
import com.jd.service.AccountService;
import org.springframework.transaction.annotation.Transactional;
?
import javax.annotation.Resource;
?
/*** Created by ${HeJD} on 2018/6/27.*/
//添加事务注解,异常的时候能保证事务的一致性
?
@Service("accountService")
public class AccountServiceImpl implements AccountService {
?@Resourceprivate AccountDao accountDao;
?@Transactional   public void transferAccounts(int fromUserId,int toUserId,float account){
?Account fromUserAccount=accountDao.getOne(fromUserId);fromUserAccount.setBalance(fromUserAccount.getBalance()-account);accountDao.save(fromUserAccount); // fromUser扣钱
?Account toUserAccount=accountDao.getOne(toUserId);toUserAccount.setBalance(toUserAccount.getBalance()+account);//假设转账的时候假如出现异常,业务类或业务方法中没有使用@Transactional控制事务,则会出现钱转出了,收钱人没有收到的情况int zero = 1/0;  //这个异常照样是打开accountDao.save(toUserAccount); // toUser加钱}
}


?
我们恢复下数据700  300

然后再重启启动类,

浏览器输入:http://localhost:8080/account/transfer

运行NO

但是数据库数据没变化 说明启动作用了。

  相关解决方案