public interface Service{
//业务方法
doBusiness();
//数据库更新方法
doBusinessTX();
}
@Service
public class ServiceImpl implements Service,ApplicationContextAware{
//自己注入自己
@Autowired
private Service ServiceProxy;
//或者使用Aware接口获取BeanFactory获取当前对象事务代理对象
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException{
this.Service = ctx.getBean(Service.class);
}
@Transactional(rollback=RuntimeException.class)//默认RuntimeException和虚拟机Error回滚
public void doBusinessTX(){
数据库更新 可抛出RuntimeException 自动触发数据库回滚
return;
}
public void operateRedis(){
//redisTemplate.eval 涉及多个KEY操作使用LUA脚本
}
public void operateRedisRollback(){
//redisTemplate.eval 执行LUA脚本 多个KEY同时操作 原子执行 回退redis修改
}
public void operateMQ() throws MQException{
//发送MQ 捕捉RuntimeException 转换为MQException再抛出 其他异常转换为MQException
}
@Override
public void doBusiness(){
try{
this.operateRedis();//先操作redis
this.ServiceProxy.doBusinessTX();//再操作数据库,代理对象调用事务方法 方法结束事务提交
this.operateMQ();//最后发送MQ 发送失败需重发 重发仍失败抛出MQException
}catch(RuntimeException e){
//运行时异常 事务方法抛出后 数据库已回滚 需手动回滚redis
this.operateRedisRollback();
}catch(MQException me){
//MQException 后发MQ,如果时先发MQ,则可能涉及数据库事务失败再发送撤销MQ,可能触发MQ关联系统回滚,增加复杂性,如先发MQ最后确认,则数据库事务期间占用MQ链接而不确认,实际效果同后发MQ,如后发MQ重试仍发送失败,通常业务状态置为异常,需手动冲正或异步定时任务处理或延迟发送,如果还有文件服务器的操作,一般不能将耗时IO操作放在数据库事务中,可异步调起文件服务操作,并可使用类似信号量限制IO。
}finally{
}
}
}