Spring boot学习笔记之五:SpringBoot之事务管理@Transactional

1. 前言

以前学ssh ssm都有事务管理service层,通过applicationContext.xml配置,在service类或所有service方法都加上事务操作;用来保证一致性,即service方法里的多个dao操作,要么同时成功,要么同时失败;

springboot下的话 搞一个@Transactional即可,无需再进行配置

2.编码

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

  1. 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

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

猜你喜欢

转载自blog.csdn.net/eeeeasy/article/details/80834992