Spring高级事务管理难点剖析

1Spring事务传播行为

所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring支持7种事务传播行为java

PROPAGATION_REQUIRED(加入已有事务)spring

若是当前没有事务,就新建一个事务,若是已经存在一个事务中,加入到这个事务中。这是最多见也是默认的方式。数据库

PROPAGATION_SUPPORTS(跟随环境)缓存

支持当前事务,若是当前没有事务,就以非事务方式执行。框架

PROPAGATION_MANDATORY(须要事务)ide

使用当前的事务,若是当前没有事务,就抛出异常。工具

PROPAGATION_REQUIRES_NEW(独立事务)测试

新建事务,若是当前存在事务,把当前事务挂起。url

PROPAGATION_NOT_SUPPORTED(非事务方式)spa

以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER(排除事务)

以非事务方式执行,若是当前存在事务,则抛出异常。

PROPAGATION_NESTED(嵌套事务)

若是当前存在事务,则在嵌套事务内执行。若是当前没有事务,则执行与PROPAGATION_REQUIRED相似的操做。


        Spring默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的状况。假设ServiveX#methodX()都工做在事务环境下(即都被Spring事务加强了),假设程序中存在以下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这3个服务类的3个方法经过Spring的事务传播机制都工做在同一个事务中。

        若是在一个ServiceA和a()方法中启动一个线程,在这个新建立的线程中执行ServiceB的事务方法b()。在相同线程中进行相互嵌套调用的事务方法工做于相同的事务中。若是这些相互嵌套调用的方法工做在不一样的线程中,不一样线程下的事务方法工做在独立的事务中。


2多种数据持久方法事务管理

        若是你采用了一个高端ORM技术(Hibernate,JPA,JDO),同时采用一个JDBC技术(Spring JDBC,iBatis),因为前者的会话(Session)是对后者链接(Connection)的封装,Spring会“足够智能地”在同一个事务线程让前者的会话封装后者的链接。因此,咱们只要直接采用前者的事务管理器就能够了。下表给出了混合数据访问技术所对应的事务管理器: 

1不一样持久方式的事务统一

     Spring提供了一个能从当前事务上下文中获取绑定的数据链接的工具类,那就是DataSourceUtils。Spring强调必须使用DataSourceUtils工具类获取数据链接。

 static Connection doGetConnection(DataSource dataSource)

首先尝试从事务上下文中获取链接,失败后再从数据源获取链接;

static Connection getConnection(DataSource dataSource)

doGetConnection方法的功能同样,实际上,它内部就是调用doGetConnection方法获取链接的;

static void  doReleaseConnection(Connection con, DataSource dataSource)

释放链接,放回到链接池中;

static void release Connection(Connection con, DataSource dataSource)

和doReleaseConnection方法的功能同样,实际上,它内部就是调用doReleaseConnection方法获取链接的;

测试demo:

@Service
public class TestTranscationServiceImpl implements TestTranscationService {

    @Autowired
    private TestTranscationDao testTranscationDao;

    @Override
    @Transactional
    public int test(){
        testTranscationDao.update1();
        testTranscationDao.update2();       
        return 0;
    }
}
@Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int update1() {
        //1.得到数据库链接
        Connection con = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
        try {
            con.prepareStatement("update grade_info set grade_name='11' where grade_id=1").executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }finally {
            //2若是当前方法没有上下文事务管理,不释放数据库链接会形成数据库链接泄露
            //若是存在上下文事务,调用或者不调用数据库链接释放都没有问题
            DataSourceUtils.releaseConnection(con, jdbcTemplate.getDataSource());
        }
        return 0;

    }

    @Override
    public int update2(){
        //3.得到数据库链接   和1的数据库链接是同一个链接
        Connection con = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
        try {
            //4.这种方法取到的数据库链接和 1,3取到的数据库链接不一样
            Connection conn = jdbcTemplate.getDataSource().getConnection();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return jdbcTemplate.update("update grade_info set grade_name='高中三年级' where grade_id=1");
    }

        Spring为每一个数据访问技术框架都提供了一个获取事务上下文绑定的数据链接(或其衍生品)的工具类和数据源(或其衍生品)的代理类。 


2Hibernate和JDBC混合使用注意事项

        因为Hibernate一级缓存的缘由,在经过save,update,delete等方法操做数据时,并无真正向数据库发送SQL,只有调用flush()时,Hibernate才会将一级缓存中的状态变化同步到数据库中。

        Hibernate的事务管理在提交事务时,会自动调用flush()操做,将一级缓存同步到数据库中,此时才会将产生并向数据库发送SQL语句。

        正是由于以上缘由的存在,全部在混合使用JDBC和Hibernate时,可能存在丢失更新的问题。

        在混合使用Hibernate和JDBC时,JDBC的操做不会同步到Hibernate的缓存中(一级缓存及二级缓存),Hibernate缓存中的状态变动也不被JDBC感知。所以混合使用时必须特别关注这一点。

        因为混合数据访问技术的方案的事务同步而缓存不一样步的状况,因此最好用Hibernate完成读写操做,而用Spring JDBC完成读的操做。如用Spring JDBC进行简要列表的查询,而用Hibernate对查询出的数据进行维护。若是确实要同时使用Hibernate和Spring JDBC读写数据,则必须充分考虑到Hibernate缓存机制引起的问题:必须充分分析数据维护逻辑,根据须要,及时调用Hibernate的flush()方法,以避免覆盖Spring JDBC的更改,在Spring JDBC更改数据库时,维护Hibernate的缓存。

3Spring的事务加强限制条件

        因为Spring事务管理是基于接口代理或动态字节码技术,经过AOP实施事务加强的。

        对于基于接口动态代理的AOP事务加强来讲,因为接口的方法是public的,这就要求实现类的实现方法必须是public的(不能是protected,private等),同时不能使用static的修饰符。因此,能够实施接口动态代理的方法只能是使用“public”或“public final”修饰符的方法,其它方法不可能被动态代理,相应的也就不能实施AOP加强,也即不能进行Spring事务加强了。

        基于CGLib字节码动态代理的方案是经过扩展被加强类,动态建立子类的方式进行AOP加强植入的。因为使用final,static,private修饰符的方法都不能被子类覆盖,相应的,这些方法将不能被实施的AOP加强。因此,必须特别注意这些修饰符的使用,以避免不当心成为事务管理的漏网之鱼。

4Spring事务管理的异常捕捉,事务回滚

        spring的事务管理器只对 unchecked exception进行异常回滚,Error和RuntimeException及其子类是unchecked exception.其余exception是checked exception.  

        若是在service层中,使用了try ,catch来捕捉异常,致使sevice层出现的异常被 “截留”,没法抛出给事务管理器,这就给事务管理器形成一种假象,就像程序在运行中,没有产生任何问题,所以也就不会对出现 runtimeException进行回滚操做。


文章来自于网上资源整理,本身通过demo测试

友情连接:

Spring 3.x企业实用开发实战》做者博客http://stamen.iteye.com/category/209694有相关系列博文

百度文库PPT: Spring高级事务管理难点难点剖析

相关文章
相关标签/搜索