所谓事务,就是让程序拥有原子性,即要么同时成功,要么同时失败。
案例
我们来看这段代码:
@Service
public class VideoOrderServiceImpl implements VideoOrderService {
@Autowired
private VideoOrderMapper videoOrderMapper;
@Autowired
private VideoMapper videoMapper;
@Autowired
private EpisodeMapper episodeMapper;
@Autowired
private PlayRecordMapper playRecordMapper;
/**
* 下单模块
* 未来版本:优惠券抵扣、风控用户检测、生成订单基础信息、生成支付信息
* @param userId
* @param videoId
* @return
*/
@Override
@Transactional
public int save(int userId, int videoId) {
// 判断是否已经购买
VideoOrder videoOrder = videoOrderMapper.findByUserIdAndOrderIdAndState(userId,videoId,1);
if (videoOrder != null){
return 0;
}
Video video = videoMapper.findById(videoId);
VideoOrder newVideoOrder = new VideoOrder();
newVideoOrder.setCreateTime(new Date());
newVideoOrder.setOutTradeNo(UUID.randomUUID().toString());
newVideoOrder.setState(1);
newVideoOrder.setTotalFee(video.getPrice());
newVideoOrder.setUserId(userId);
newVideoOrder.setVideoId(videoId);
newVideoOrder.setVideoImg(video.getCoverImg());
newVideoOrder.setVideoTitle(video.getTitle());
int rows = videoOrderMapper.saveOrder(newVideoOrder);
// 生成播放记录
if (rows == 1){
Episode episode = episodeMapper.findFirstEpisodeByVideoId(videoId);
if (episode == null){
throw new XDException(-1,"视频没有集信息,请运营人员检查");
}
PlayRecord playRecord = new PlayRecord();
playRecord.setCreateTime(new Date());
playRecord.setEpisodeId(episode.getId());
playRecord.setCurrentNum(episode.getNum());
playRecord.setUserId(userId);
playRecord.setVideoId(videoId);
playRecordMapper.saveRecord(playRecord);
}
return rows;
}
}
这是我现在写的K12项目中的一段接口实现类代码。这段代码的功能相信大家看注释就能看出来,就是用户在下单时同时生成播放记录,当然刚下单时的播放记录是从第一个视频开始的
那么,如果在这两个逻辑之前出现了异常,比如我在【// 生成播放记录】该注解的上一行加上“int i = 1/0;” 显然“int i = 1/0;”会导致代码异常。
那该程序在执行异常代码之前的生成订单逻辑的代码中是没有异常的,但是执行完该逻辑后,发生了异常,那么【生成播放记录】肯定不会执行成功。
如果你看数据库会发现,订单确实生成了,但是播放记录没有生成,这显然不符合实际需求。
解决方法
开启事务控制:
- 在启动类上加上注解 @EnableTransactionManagement
- 在业务类,或者业务方法上加注解 @Transactional
OK,只需要这简单的两个注解即可完成!
但是注意,最好在有需求的业务方法上加 @Transactional,因为在业务类加该注解是默认开启该业务类中的全部方法事务,影响性能。
这样如果两个逻辑之间,或者其中一个逻辑出现了异常,那么都不会执行成功,则数据库也不会添加数据。