用实例来演示spring事务
我们模拟电商形式来验证和实践spring的事务功能
数据库准备
sale表
goods表
idea环境准备(略)
环境和前面的笔记内容一直
java代码准备
1、实体类根据数据库来创建(略)
2、dao包
GoodsDao
public interface GoodsDao {
//更新库存
int updateGoods(Goods goods);//goods表示本次用户购买的商品信息:id、购买数量
Goods selectGoods(Integer id);//查询商品信息
}
GoodsDao的mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.trans.dao.GoodsDao">
<select id="selectGoods" resultType="com.trans.domain.Goods">
select * from goods where id=#{gid}
</select>
<update id="updateGoods">
update goods set amount = amount - #{amount} where id=#{id}
</update>
</mapper>
SaleDao
public interface SaleDao {
int insertSale(Sale sale);//增加销售记录
}
SaleDao的mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.trans.dao.SaleDao">
<insert id="insertSale">
insert into sale(gid,nums) values(#{gid},#{nums});
</insert>
</mapper>
3、service包
BuyGoodsService
public interface BuyGoodsService {
void buy(Integer goodsId,Integer nums);//购买商品
}
BuyGoodsServiceImpl
public class BuyGoodsServiceImpl implements BuyGoodsService {
private SaleDao saleDao;
private GoodsDao goodsDao;
public void buy(Integer goodsId, Integer nums) {
System.out.println("buy方法开始=========");
//记录销售信息,向sale表更新信息
Sale sale=new Sale();
sale.setGid(goodsId);
sale.setNums(nums);
saleDao.insertSale(sale);
//更新库存
Goods goods=goodsDao.selectGoods(goodsId);
if(goods == null){
throw new NullPointerException(goodsId+"不存在");
}else if(goods.getAmount() < nums){
throw new NotEnoughException(goodsId+"库存不足");
}
//修改库存
Goods buyGoods=new Goods();
buyGoods.setId(goodsId);
buyGoods.setAmount(nums);
goodsDao.updateGoods(buyGoods);
System.out.println("buy方法结束=========");
}
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
}
4、excep包
我们创建一个异常类,继承运行时异常
public class NotEnoughException extends RuntimeException {//自定义运行时异常
public NotEnoughException() {
super();
}
public NotEnoughException(String message) {
super(message);
}
}
5、测试类
public class MyTest {
@Test
public void test01(){
String con="applicationContext.xml";
ApplicationContext ctx= new ClassPathXmlApplicationContext(con);
BuyGoodsService bb= (BuyGoodsService) ctx.getBean("buyservice");
bb.buy(1001,1);
}
}
调试
当我们的商品id和购买数量都符合程序要求时,可以正常执行购买操作(结果图就不放了);当id或购买量有错误时,程序会返回事先写好的异常语句,且虽然goods表没有进行改动,但是我们可以看到sale表是有记录的(goods表只有两个商品,1001和1002;并且数量都只有几十个,当我们传入的id和nums超出范围,虽然goods表没有改动,但是sale表却记录下来了)
如果我们想当出现错误异常时,不但不修改goods表,我们也不想在sale表看到记录,这时候就要用到事务了
使用spring事务的步骤
方法一:注解(适合中小项目使用)
spring框架自己用aop实现给业务方法增加事务的功能,使用
@Transactional
注解(spring框架自己的注解)增加事务;放在public方法的上面,表示当前方法具有事务,可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等
1. spring配置文件中声明事务管理器对象
```
<!--spring声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--告知连接的数据库(数据源)-->
<property name="dataSource" ref="myDataSource"/>
</bean>
```
2. 在spring中开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务
spring使用aop机制,创建
@Transactional
所在的类代理对象,给方法加入事务的功能;spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知,例如:
@Around("你要增加的事务功能的业务方法名称")
Object myAround(){
开启事务,spring给你开启
try{
buy(1001,10);
spring的事务管理器.commit();
}catch(Exception e){
spring的事务管理器.rollback();
}
}
下面是具体实现
<tx:annotation-driven transaction-manager="transactionManager"/>
3. 在目标方法的上面加入@Trancational
我们在BuyGoodsServiceImpl
类的buy()
方法上加上@Trancational
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,NotEnoughException.class
}
)
propagation
:用于设置事务传播属性。该属性类型为Propagation
枚举,默认值为
Propagation.REQUIRED
isolation
:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为
Isolation.DEFAULT
readOnly
:用于设置该方法对数据库的操作是否是只读的。该属性为boolean
,默认值
为false
rollbackFor
:指定需要回滚的异常类。类型为Class[]
,默认值为空数组。当然,若只有一个异常类时,可以不使用数组
一般我们用
select
查询时,readOnly
才设置为true
;rollbackFor
表示发生指定异常一定回滚,spring执行时,先判断抛出的异常是否被包含在rollbackFor
中,是则一定回滚,无论什么类型的异常;如果抛出的异常不在rollbackFor
中,则spring会判断是否为RuntimeException
,是则一定回滚
上面我们用到的都是默认值,所以可以直接写@Transactional
添加事务后调试
我们先正常运行,操作前↓
@Test
public void test01(){
String con="applicationContext.xml";
ApplicationContext ctx= new ClassPathXmlApplicationContext(con);
BuyGoodsService bb= (BuyGoodsService) ctx.getBean("buyservice");
bb.buy(1002,10);
}
正常操作后
我们试着传入错误参数(错误id或错误nums)
@Test
public void test01(){
String con="applicationContext.xml";
ApplicationContext ctx= new ClassPathXmlApplicationContext(con);
BuyGoodsService bb= (BuyGoodsService) ctx.getBean("buyservice");
bb.buy(1004,10);
}