为什么需要事务?
- 事务管理是开发的时候必用的一个功能。
- 事务管理用来确保数据的完整性和一致性。
- 事务管理就是一系列的动作,他们被当做一个单独的工作单元,这些动作要么全部完成,要么全部不起作用。
本文仅仅是完成一个准备工作,下一篇文章才来阐述如何进行事务管理。
一、建立以下表
- book(bookid,bookname,price)(1,mysql,100)(2,java,50)
- bookstock(bookid,amount)(1,100)(2,80)
- buyer(buyerid,buyername,money)(1,xie,160)(2,yxin,520)
二、写一个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package tx; public interface bookshopDao { // 根据书号获取单价 public int findpricebybookid(String bookid); // 更新书的库存,使书号对应的库存 -1 public void updatebookstock(String bookid); // 更新账户余额,使 buyer 的 money - price public void updatebuyermoney(String buyername, int price); } |
三、写一个实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
package tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("bookshopdao") public class bookshopdaoimpl implements bookshopDao { @Autowired private JdbcTemplate JdbcTemplate = null; @Override public int findpricebybookid(String bookid) { String sql = " select price from book where bookid = ? "; return JdbcTemplate.queryForObject(sql, Integer.class, bookid); } @Override public void updatebookstock(String bookid) { String sql = " update bookstock set amount = amount - 1 where bookid = ? "; JdbcTemplate.update(sql, bookid); } @Override public void updatebuyermoney(String buyername, int price) { String sql = " update buyer set money = money - ? where buyername = ? "; JdbcTemplate.update(sql,price,buyername); } } |
四、写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!-- 导入资源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置c3p0数据源 --> <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 配置spring的 jdbctemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="datasource"></property> </bean> <context:component-scan base-package="tx"></context:component-scan> |
五、写一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package tx; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test { private ApplicationContext applicationContext = null; private bookshopDao bookshopDao = null; { applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml"); bookshopDao = (tx.bookshopDao) applicationContext.getBean("bookshopdao"); } @Test public void testfindprice() { System.out.println(bookshopDao.findpricebybookid("2")); } @Test public void testupdatebookstock() { bookshopDao.updatebookstock("1"); } @Test public void testupdatebuyermoney(){ bookshopDao.updatebuyermoney("xie", 100); } } |
成功运行了三个方法。
但是在mysql中,很多东西都可以是负的。
比如我xie只有160块钱,但是可以买2本100的mysql。不但能买到2本,xie的钱可以变成-40,买多了书的库存甚至能变成负的。
这些现象都是我不想看到的,所以需要手工抛出异常。
六、写出错误类
两种类型的错误:库存错误、金额错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package tx; public class bookstockexception extends RuntimeException { public bookstockexception() { super(); // TODO Auto-generated constructor stub } public bookstockexception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public bookstockexception(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public bookstockexception(String message) { super(message); // TODO Auto-generated constructor stub } public bookstockexception(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package tx; public class buyermoneyexception extends RuntimeException { public buyermoneyexception() { super(); // TODO Auto-generated constructor stub } public buyermoneyexception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public buyermoneyexception(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public buyermoneyexception(String message) { super(message); // TODO Auto-generated constructor stub } public buyermoneyexception(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } } |
七、写出报错逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("bookshopdao") public class bookshopdaoimpl implements bookshopDao { @Autowired private JdbcTemplate JdbcTemplate = null; @Override public int findpricebybookid(String bookid) { String sql = " select price from book where bookid = ? "; return JdbcTemplate.queryForObject(sql, Integer.class, bookid); } @Override public void updatebookstock(String bookid) { String sql2 = " select amount from bookstock where bookid = ? "; int stock = JdbcTemplate.queryForObject(sql2, Integer.class, bookid); if (stock == 0) { throw new bookstockexception("库存不足"); } else { String sql = " update bookstock set amount = amount - 1 where bookid = ? "; JdbcTemplate.update(sql, bookid); } } @Override public void updatebuyermoney(String buyername, int price) { String sql2 = " select money from buyer where buyername = ? "; int money = JdbcTemplate.queryForObject(sql2, Integer.class, buyername); if (money > price) { String sql = " update buyer set money = money - ? where buyername = ? "; JdbcTemplate.update(sql, price, buyername); } else { throw new buyermoneyexception("余额不足"); } } } |
八、整合整个购买流程
新建接口类:
1 2 3 4 5 6 |
package tx; public interface bookshopservice { public void purchase(String buyername,String bookid); } |
实现接口类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("bookshopservice") public class bookshopserviceimpl implements bookshopservice { @Autowired private bookshopDao bookshopDao; @Override public void purchase(String buyername, String bookid) { int price = bookshopDao.findpricebybookid(bookid); bookshopDao.updatebookstock(bookid); bookshopDao.updatebuyermoney(buyername, price); } } |
自动装配bookshopDao。
⑨、更改测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package tx; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class test { private ApplicationContext applicationContext = null; private bookshopservice bookshopservice = null; { applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml"); bookshopservice = (tx.bookshopservice) applicationContext.getBean("bookshopservice"); } @Test public void purchase(){ bookshopservice.purchase("xie", "1"); } } |
- 这段程序运行的时候会先把书的库存-1,如果库存不足,则爆出库存不足的错误,运行停止。
- 如果库存充足,则会继续往下运行,如果购买者余额足够,则会扣除相应的金额,如果余额不够,则会爆出余额不足的错误。
那么问题来了,虽然余额不足报错了,但是库存已经-1了,出现了购买者没有成功购买,但是库存已经减少了的异常情况,整个购买流程没有作为一个整体事务进行处理,导致系统出错。
这就是为什么我们需要spring事务管理。spring可以帮助我们管理实务,保证一致性。
二、总结
下一篇文章将会详细讨论。