Spring Transactional

原作者:尚硅谷-佟刚


数据表:


account 表(用户名 ,余额)
这里写图片描述

book_stock(编号 ,库存)
这里写图片描述

book (编号,书名,价格)
这里写图片描述



BookShopDao .java

package com.atguigu.spring.tx;

public interface BookShopDao {
    //根据书号获取书的单价
    public int findBookPriceByIsbn(String isbn);

    //更新书的库存.-1
    public void updateBookStock(String isbn);

    //更新用户的账户余额 balance - price
    public void updateUserAccount(String username,int price);
}

BookShopDaoImpl .java

package com.atguigu.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;


@Repository
public class BookShopDaoImpl implements BookShopDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int findBookPriceByIsbn(String isbn) {
        String sql="SELECT price FROM book WHERE isbn=?";
        return jdbcTemplate.queryForObject(sql, Integer.class, isbn);
    }

    @Override
    public void updateBookStock(String isbn) {
        //验证库存
        String sql2="SELECT stock FROM book_stock WHERE isbn=?";

        int stock=jdbcTemplate.queryForObject(sql2,Integer.class,isbn);
        if(stock==0){
            throw new BookStockException("库存不足!");
        }

        String sql="UPDATE book_stock SET stock=stock-1 WHERE isbn=?";
        jdbcTemplate.update(sql, isbn);
    }

    @Override
    public void updateUserAccount(String username, int price) {
          //验证余额
          String sql2="SELECT balance FROM Account WHERE username=?";
          int balance=jdbcTemplate.queryForObject(sql2,Integer.class,username);
          if(balance<price){
              throw new UserAccountException("余额不足!");
          }

          String sql="UPDATE account SET balance=balance-? WHERE username=?";
          jdbcTemplate.update(sql,price, username);
    }

}

BookShopService .java

package com.atguigu.spring.tx;

public interface BookShopService {
    public void purchase(String username,String isbn);
}

BookShopServiceImpl .java

package com.atguigu.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class BookShopServiceImpl implements BookShopService {

    @Autowired
    private BookShopDao bookShopDao;

    //propagation:事物的传播行为
    //事务的隔离机制:isolation
    //默认Spring对运行时异常进行回滚
    //readOnle事物的只读属性
    //timeout事务强制回滚之前的时间

//  @Transactional(propagation=Propagation.REQUIRES_NEW,
//          isolation=Isolation.READ_COMMITTED,
//          noRollbackForClassName={"com.atguigu.spring.tx.UserAccountException"}
//          )
    @Transactional(propagation=Propagation.REQUIRED,
    isolation=Isolation.READ_COMMITTED,
    readOnly=false,
    timeout=3)
    @Override
    public void purchase(String username, String isbn) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

       //1.获取书价
       int price=bookShopDao.findBookPriceByIsbn(isbn);

       //2.更新库存
       bookShopDao.updateBookStock(isbn);

       //3.更新用户余额
       bookShopDao.updateUserAccount(username, price);
    }

}

Cashier.java

package com.atguigu.spring.tx;

import java.util.List;
public interface Cashier {

    public void checkout(String username,List<String> isbns);

}

CashierImpl .java

package com.atguigu.spring.tx;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


@Service
public class CashierImpl implements Cashier {
    @Autowired
    private BookShopService bookShopService;

    @Transactional
    @Override
    public void checkout(String username, List<String> isbns) {
           for(String isbn:isbns){
               bookShopService.purchase(username, isbn);
           }
    }

}

BookStockException .java

package com.atguigu.spring.tx;


public class BookStockException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    public BookStockException() {
        super();
    }

    public BookStockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public BookStockException(String message, Throwable cause) {
        super(message, cause);
    }

    public BookStockException(String message) {
        super(message);
    }

    public BookStockException(Throwable cause) {
        super(cause);
    }

}

UserAccountException .java

package com.atguigu.spring.tx;

public class UserAccountException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public UserAccountException() {
        super();
    }

    public UserAccountException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public UserAccountException(String message, Throwable cause) {
        super(message, cause);
    }

    public UserAccountException(String message) {
        super(message);
    }

    public UserAccountException(Throwable cause) {
        super(cause);
    }

}

SpringTransactionTest .java

package com.atguigu.spring.tx;

import java.util.Arrays;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class SpringTransactionTest {
    private ApplicationContext ctx=null;
    private BookShopDao bookShopDao=null;
    private BookShopService bookShopService=null;
    private Cashier cashier=null;
    {
        ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        bookShopDao=ctx.getBean(BookShopDao.class);
        bookShopService=ctx.getBean(BookShopService.class);
        cashier=ctx.getBean(Cashier.class);
    }
    @Test
    public void testTransactionlPropagation(){
        cashier.checkout("aa", Arrays.asList("1001","1002"));
    }

    @Test
    public void testBookShopService(){
        bookShopService.purchase("aa", "1001");
    }

    @Test
    public void testFindBookPriceByIsbn() {
        System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
    }

    @Test
    public void testUpdateBookStock(){
          bookShopDao.updateBookStock("1001");
    }

    @Test
    public void testUpdateUserAccount(){
        bookShopDao.updateUserAccount("aa",200);
    }

}

重点:
REQUIRED:如果有事务,那么加入事务,没有的话新建一个

propagation=Propagation.REQUIRED时,单元测试
testTransactionlPropagation()方法时,accouct,book_stock表数据不会改变,因为(当 bookShopService的 purchase() 方法被另一个事务方法 checkout() 调用时, 它默认会在现有的事务内运行. 这个默认的传播行为就是 REQUIRED. 因此在 checkout() 方法的开始和终止边界内只有一个事务. 这个事务只在 checkout() 方法结束的时候被提交, 结果用户一本书都买不了)

REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
propagation=Propagation.REQUIRES_NEW时,单元测试
testTransactionlPropagation()方法时,book_stock表的1001库存 -1(10==>9), 1002 库存不变(10) ,account 的余额-100(130==>30)
控制台抛出自定义异常: com.atguigu.spring.tx.UserAccountException: 余额不足!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值