深入Spring数据库事务管理

来源:互联网 发布:五轴联动数控机床编程 编辑:程序博客网 时间:2024/06/16 11:05

相关问题

一、@Transactional的失效问题

 1.对于静态(static)方法和非public方法,注解@Transactional是失效的。

 2.自调用,就是一个类的一个方法去调用自身另外一个方法的过程。如下:

@Autowiredprivate RoleDao roleDao;@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)public int insertRole(Role role){    return roleDao.insertRole(role);}@Override@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)public int insertRoleList(List<Role> roleList) {    int count = 0;    for (Role role:roleList) {        try {            //调用自身类的方法,产生自调用问题            insertRole(role);            count++;        }catch (Exception ex){            log.info(ex);        }    }    return count;}
分析:角色插入两次都使用的是同一事务,也就是说在insertRole方法上标注的@Transactional失效了。原因在于AOP的实现原理,这里不再赘述。

解决:①使用两个服务类。【推荐】

@Servicepublic class RoleServiceImpl implements RoleService {    @Autowired    private RoleDao roleDao;    @Override    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)    public int insertRole(Role role) {        return roleDao.insertRole(role);    }}
②从IOC容器中获取RoleService代理对象。【不推荐,从容器获取代理对象有侵入之嫌,我们需要依赖于SpringIOC容器】
@Override@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)public int insertRoleList(List<Role> roleList) {    int count = 0;    //从容器中获取RoleService对象,实际是一个代理对象    RoleService roleService = ctx.getBean(RoleService.class);    for (Role role:roleList) {        try {            insertRole(role);            count++;        }catch (Exception ex){            log.info(ex);        }    }    return count;}


二、典型错误用法

1.错误使用Service

场景:在一个Controller中插入两个角色,并且两个角色需要在同一个事务中处理。

错误代码:

public class RoleController {    @Autowired    private RoleService roleService;    public void errorUseServices(){        Role role = new Role();        role.setRoleName("role_name_");        role.setNote("note_");       roleService.insertRole(role);        Role role2 = new Role();        role2.setRoleName("role_name_2");        role2.setNote("note_2");        roleService.insertRole(role2);    }}
分析:如果这个service标注用@Transactional,那么它就会启用一个事务,而一个service方法完成后,它就会释放该事务,所以前后两个insertRole是在两个不同的事务中完成的。这样如果第一个插入成功了,第二个插入失败了,就会使数据库数据库数据不完全同时成功或者失败,可能产生严重的数据不一致的问题。

2.过长时间占用事务

场景:在插入角色后还需要操作一个文件,比如处理图片的上传之类的操作。

代码:

@Override@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)public int insertRole(Role role) {    //return roleDao.insertRole(role);    int result = roleDao.insertRole(role);    //做一些与数据库无关的操作    doSomethingForFile();    return result;}
分析:当insertRole方法结束后Spring才会释放数据库事务资源,也就是说要等到doSomethingForFile()方法执行完成后,返回result后才会关闭数据库资源。高并发情况下就会出现卡顿状态,甚至因得不到数据库资源而导致系统宕机。

解决:这个方法放在controller中执行

@RequestMapping("/addRole")@ResponseBodypublic Role addRole(Role role){    roleService.insertRole(role);    //做一些与数据库无关的操作    doSomethingForFile();    return role;}

3.错误捕捉异常

场景:购买商品。其中ProductService是产品服务类,而TransactionService是记录交易信息,需求就是产品减库存和保存交易在同一个事务例,要么同时成功,要么同时失败。假设传播行为都是REQUIRED。

代码:

@Autowiredprivate ProductService productService;@Autowiredprivate TransactionService transactionService;@Override@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)public int doTransaction(TransactionBean trans){    int result = 0;    try {        //介绍库存        productService.decreaseStock(trans.getProductId, trans.getQuantity());        //如果介绍库存成功则保存记录        if(result>0){            transactionService.save(trans);        }    }catch (Exception ex){        //自行处理异常代码        //记录异常日志        log.info(ex);    }    return result;}
分析:这里的问题是方法已经存在异常了,由于开发者不了解Spring的事务约定,在两个操作方法里面加入了自己的try.catch...语句,就可能发生这样的结果:当减少库存成功了,但是保存交易信息时失败而发生了异常。由于加入的try.catch...语句,Spring在数据库事务所约定的流程中再也得不到任何异常信息了,此时Spring就会提交事务,啊这样就出现了库存减少,而交易记录却没有的糟糕情况。

解决:在catch中自行抛出异常,这样在Spring的事务流程汇总,就会捕捉到这个异常,进行事务回滚。

@Autowiredprivate ProductService productService;@Autowiredprivate TransactionService transactionService;@Override@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)public int doTransaction(TransactionBean trans){    int result = 0;    try {        //介绍库存        productService.decreaseStock(trans.getProductId, trans.getQuantity());        //如果介绍库存成功则保存记录        if(result>0){            transactionService.save(trans);        }    }catch (Exception ex){        //自行处理异常代码        //记录异常日志        log.info(ex);        //自行抛出异常,让Spring事务管理流程获取异常,进行事务管理        throw new RuntimeException(ex);    }    return result;}













原创粉丝点击