spring-transaction-tx (买书案例)

 

基础配置(准备工作)

准备三张数据表和相关数据

表:

book       书信息表
-------------------------------------------
book  | CREATE TABLE `book` (
  `isbn` varchar(50) NOT NULL,
  `book_name` varchar(100) DEFAULT NULL,
  `price` int(11) DEFAULT NULL,
  PRIMARY KEY (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

+------+-----------+-------+
| isbn | book_name | price |
+------+-----------+-------+
| 1001 | Java      |   100 |
| 1002 | Oracle    |    70 |
+------+-----------+-------+

account    用户余额表
--------------------------------------------
account | CREATE TABLE `account` (
  `username` varchar(32) DEFAULT NULL,
  `balance` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

+----------+---------+
| username | balance |
+----------+---------+
| AA       |     200 |
+----------+---------+

book_stock  书库存表
----------------------------------------------
book_stock | CREATE TABLE `book_stock` (
  `isbn` varchar(50) NOT NULL,
  `stock` int(11) DEFAULT NULL,
  PRIMARY KEY (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

+------+-------+
| isbn | stock |
+------+-------+
| 1001 |     9 |
| 1002 |    10 |
+------+-------+

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

导入jar包:

c3p0-0.9.5.2.jar
commons-dbutils-1.7.jar
commons-logging-1.2.jar
hamcrest-core-1.3.jar
junit-4.12.jar
mchange-commons-java-0.2.11.jar
mysql-connector-java-5.1.47.jar
org.springframework.transaction-3.1.2.RELEASE.jar
spring-aop-4.3.18.RELEASE.jar
spring-beans-4.3.18.RELEASE.jar
spring-context-4.3.18.RELEASE.jar
spring-context-support-4.3.18.RELEASE.jar
spring-core-4.3.18.RELEASE.jar
spring-expression-4.3.18.RELEASE.jar
spring-jdbc-4.3.18.RELEASE.jar
spring-test-4.3.18.RELEASE.jar

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

applicationContext.xml




    
    
    
         
         
         
         
    

    
         
    

    
    
         
    

    
    

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

db.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/shop?useSSL=false
jdbc.user=root
jdbc.password=root

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

两个自定义异常类

UserAccountException  用户余额不足是抛出异常

public class UserAccountException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    public UserAccountException() {
    }

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

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

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

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

BookStockException 当库存不足抛出异常

public class BookStockException extends RuntimeException{

    private static final long serialVersionUID = 1L;
    public BookStockException() {
    }

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

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

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

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

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

接口 提供底层方法

BookShopDao
public interface BookShopDao {

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

    //更新书的库存 使书号对应的库存-1
    public void updateBookStock(String isbn);

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

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

    public void checkout(String username,List isbns);
}

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

接口实现

BookShopDaoImpl
@Repository("bookShopDao")
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 sql = "select stock from book_stock where isbn =  ?";
        Integer integer = jdbcTemplate.queryForObject(sql, Integer.class, isbn);
        if(integer == 0 ){
            throw  new BookStockException("库存不足");
        }

        String sql1 = "update book_stock set stock = stock - 1 where isbn = ? ";
        jdbcTemplate.update(sql1,isbn);
    }

    @Override
    public void updateUserAccount(String username,int price) {
        //验证余额是否足够 若不足 则抛出异常
        String sql1 = "select balance from account where username =  ?";
        Integer integer = jdbcTemplate.queryForObject(sql1, Integer.class, username);
        if(integer < price ){
            throw  new UserAccountException("余额不足");
        }

        String sql = "update account set balance = balance - ? where username = ? ";
        jdbcTemplate.update(sql,price,username);
    }
}
BookShopServiceImpl
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService{

    @Autowired
    private BookShopDao bookShopDao;

    //使用propagation 指定事物的传播行为,即当前的事物方法被另一个事物方法调用时
    //如何使用事物,默认取值为REQUIRED 即使用调用方法的事物
    //REQUIRES_NEW使用自己的事物 调用事物方法的事物被挂起
    //使用isolation 指定事物的隔离级别,最常用的取值为 READ_COMMITTED  读已提交
    //默认情况下Spring的声明事物对所有运行时异常进行回滚,也可以通过对应的属性进行设置        通常情况下取默认值
    //使用readOnly  指定事物是否为只读   (如果只是读取事物,不用写,就可以不加锁,加快速度,优化)  readOnly = true
    //使用timeout指定强制回滚之前 事物可以占用的时间  优化事物占用链接时间
    //添加事物注解
    @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED,
    noRollbackFor = {UserAccountException.class},readOnly = false,timeout = 3)
    @Override
    public void purchase(String username, String isbn) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //获取书的单价
        int price = bookShopDao.findBookPriceByIsbn(isbn);
        //更新书的库存
        bookShopDao.updateBookStock(isbn);
        //更新用户的余额
        bookShopDao.updateUserAccount(username,price);
    }
}
CashierImpl

@Service("cashier")
public class CashierImpl implements Cashier {

    @Autowired
    private BookShopService bookShopService;

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

+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+

SpringTransationTest 运行方法
public class SpringTransationTest {

    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 testUser(){
        bookShopDao.updateUserAccount("AA",100);
    }

    @Test
    public void BookShopService(){
        bookShopService.purchase("AA","1001");
    }

    @Test
    public void testTransactionPropagation(){
        cashier.checkout("AA", Arrays.asList("1001","1002"));
    }
}

 

你可能感兴趣的:(j)