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
@GeneratedValue
private 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 {
@Resource
private 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 {
@Resource
private 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 {
@Resource
private 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 {
@Resource
private 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
但是数据库数据没变化 说明启动作用了。