现在的位置: 首页 > 综合 > 正文

事务使用分析经验总结

2013年10月31日 ⁄ 综合 ⁄ 共 3489字 ⁄ 字号 评论关闭

 

前段时间版本开发中,同事问我,为什么他的功能没有事务回滚,由于事务问题一直在项目中存在,今天抽空了解决了该问题,下面是分析结果
分析过程是这样的:
1.模拟当时没有事务回滚的代码写了个测试代码
   在xhttp层的user类中加了个testtran方法
   在facade层的user类中加了个facadetran方法和servicetran方法,facadetran方法调用servicetran方法
   在service层的user类中加了个testtran方法,里面有更新和插入操作
配置applicationcontext-datasource.xml文件,关键代码
 
在配置中对facadetran和servicetran方法都进行了拦截。
配置application-facade.xml文件,这个同以前的配置是一样的,主要是用于事务控制的
框中部分就是最基本的spring事务配置,这里就不说了
 
2.测试结果
1.去掉facade* 这个匹配项,则事务不启作用,虽然有service *的匹配项
2.加上facade* 这个匹配项,有事务
 
3.结果分析
这是有事务时的当前本地变量中存的connection连接对象
start=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)    --facade 的facadetran打印的
start service=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)  --facade 的servicetran打印的
updateTrance=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)  --dao层打印的
addTrance=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)   --dao层打印的
end service=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)  --facade 的servicetran打印的
end=10437382(oracle.jdbc.driver.OracleConnection@1e9445c)   ----facade 的facadetran打印的
所有的connection对象是一样的,因此保证了事务
 
这是没有事务时的当前本地变量中存的connection连接对象
start=5067839(oracle.jdbc.driver.OracleConnection@d62eb9)   --facade 的facadetran打印的
start service=26067327(oracle.jdbc.driver.OracleConnection@19a7cb8)  --facade 的servicetran打印的
updateTrance=11042980(oracle.jdbc.driver.OracleConnection@959dae)  --dao层打印的
addTrance=31319257(oracle.jdbc.driver.OracleConnection@db6fd3)  --dao层打印的
end service=27130714(oracle.jdbc.driver.OracleConnection@e10f10)  --facade 的servicetran打印的
end=25340210(oracle.jdbc.driver.OracleConnection@1323b6c)  ----facade 的facadetran打印的
每个connection对象都不一样,因此没有了事务
 
4.结论:
如果需要事务,请在最外层调入facade层时的方法时做事务,如facadeTran,而后面过滤到的也是没用的。因此这个开发时一定要注意,如果还是没有事务,可以直接打印当前的
连接对象是不是正确的
 
5.spring事务是这样实现的?
理解这个也对我们找问题也是有帮助的,假设如果我们自已实现一个事务也是可以的,spring也只是借助数据库来回滚的。
a.得到一个连接conn
b.把事务改成手动conn.setAutoCommit(false)
c.数据库相关业务操作
d.提交conn.commit();
e.如果业务失败,则在异常中comm.rollback();
f.最后需要在finally关闭连接
因此如果几个业务需要同时提交时必须在一个连接的原因了,spring只不过在这个流程中穿了个外套而已,呵呵
 
6.spring的事务传播行为介绍
  • PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
  • PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。
  •  
    7.问题补充
    ......
    String[] arr = str.split(",");
            try
            {
                getSqlMapClient().startTransaction();
                getSqlMapClient().startBatch();
                for (int i = 0; i < 3; i++)
                {
                    String serid = arr[0];
                    String name = arr[1];
                    Map<String, String> map = new HashMap<String, String>();
                    map.put("serid", serid + new Random().nextInt(10000));
                    map.put("name", name);
                    System.out.println("addTrance="
                            + this.getDataSource().getConnection());
                    getSqlMapClient().insert("AppInfoFor139.add", map);
                }
                getSqlMapClient().executeBatch();
                getSqlMapClient().commitTransaction();
            }
            catch (SQLException e)
            {
                throw new SepException(ErrorCode.INSERT_EXCEPTION, true, e);
            }
    ....
     
    象这种批处理,如果在facade打开了事务,这里手动设置也是无效的,因此需要注意这种需要手动提交的,就不能在facade开启事务了
     
    8.开发建议
    开发上面需要做下规范
    1、不能在接口层进行for循环调用facade层的方法,特别是做了事务代理的方法。这样从facade层就获取了一个db的connection。大量的组装url for循环调用导致了业务向数据库发了大量的获取连接和释放连接的不需要的开销)
     
    2、facade层定义的方法如果涉及到数据库操作且需要进行事务处理,需要和spring的bin定义文件中的事务代理类的add*之类的正则表达式一致。
     
    以上如果有错误,请大牛们纠正,呵呵

    抱歉!评论已关闭.