当前位置: 首页 > 科技观察

Spring高级事务管理难点分析

时间:2023-03-17 15:48:18 科技观察

1.Spring事务传播行为所谓事务传播行为就是当多个事务方法相互调用时,事务如何在这些方法之间传播。Spring支持7种事务传播行为PROPAGATION_REQUIRED(加入已有事务)。如果没有当前事务,则创建一个新事务。如果已经有交易,加入它。这是最常见和默认的方式。PROPAGATION_SUPPORTS(跟随环境)支持当前事务,如果没有当前事务,则以非事务方式执行。PROPAGATION_MANDATORY(需要有事务)使用当前事务,如果没有当前事务则抛出异常。PROPAGATION_REQUIRES_NEW(独立事务)创建一个新事务,如果有当前事务,则暂停当前事务。PROPAGATION_NOT_SUPPORTED(非事务模式)以非事务模式执行操作。如果有当前事务,则暂停当前事务。PROPAGATION_NEVER(排除事务)以非事务方式执行,如果当前存在事务则抛出异常。PROPAGATION_NESTED(嵌套事务)如果事务当前存在,则在嵌套事务内执行。如果没有当前事务,则执行与PROPAGATION_REQUIRED类似的操作。Spring默认的事务传播行为是PROPAGATION_REQUIRED,适用于大多数情况。假设ServiveX#methodX()工作在事务环境下(即都是Spring事务增强的),假设程序中存在如下调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这三个服务类的三个方法都通过Spring的事务传播机制工作在同一个事务中。如果在一个ServiceA和a()方法中启动了一个线程,ServiceB的事务方法b()就在这个新创建的线程中执行。在同一个线程中相互嵌套调用的事务方法在同一个事务中工作。如果这些相互嵌套的调用方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。2.多种数据持久化方式事务管理如果同时使用一种高端的ORM技术(Hibernate、JPA、JDO)和一种JDBC技术(SpringJDBC、iBatis),由于前者的会话(Session)连接到后者(Connection),Spring会“足够聪明”地让前者的session将后者的connection封装在同一个事务线程中。因此,我们只需要直接采用之前的事务管理器即可。下表是混合数据访问技术对应的事务管理器:1)统一不同持久化方式的事务Spring提供了一个工具类,可以从当前事务上下文中获取绑定的数据连接,即DataSourceUtils。Spring强调必须使用DataSourceUtils工具类来获取数据连接。staticConnectiondoGetConnection(DataSourcedataSource)首先尝试从事务上下文中获取连接,失败后再从数据源中获取连接;staticConnectiongetConnection(DataSourcedataSource)doGetConnection方法的作用是一样的,其实都是在内部调用doGetConnection方法来获取连接;staticvoiddoReleaseConnection(Connectioncon,DataSourcedataSource)释放连接并放回连接池;staticvoidreleaseConnection(Connectioncon,DataSourcedataSource)与doReleaseConnection方法功能相同。其实就是内部调用doReleaseConnection方法获取连接;测试demo:@ServicepublicclassTestTranscationServiceImplimplementsTestTranscationService{@AutowiredprivateTestTranscationDaotestTranscationDao;@Override@Transactionalpublicinttest(){testTranscationDao.update1();testTranscationDao.update2();返回0;}@AutowiredprivateJdbcTemplatejdbcTemplate;@Overridepublicintupdate1(){//1.获得数据库连接Connectioncon=DataSourceUtils.getConnection(jdbcTemplate.getDataSource());try{con.prepareStatement("updategrade_infosetgrade_name='11'wheregrade_id=1").executeUpdate();}catch(SQLExceptione){通过wnewRuntimeException(e);}finally{//2如果当前方法没有上下文事务管理,如果数据库连接没有释放,数据库连接就会泄露//如果有上下文事务,调用或不调用数据库连接都没有问题释放DataSourceUtils.releaseConnection(con,jdbcTemplate.getDataSource());}返回0;}@Overridepublicintupdate2(){//3.获取数据库连接和1的数据库连接是同一个连接Connectioncon=DataSourceUtils.getConnection(jdbcTemplate.getDataSource());try{//4、该方法获取的数据库连接与1、3获取的数据库连接不同,Connectionconn=jdbcTemplate.getDataSource().getConnection();conn.close();}catch(SQLExceptione){e.printStackTrace();}returnjdbcTemplate.update("updategrade_infosetgrade_name='Grade3'wheregrade_id=1");}Spring为数据源(或其派生类)提供了一个工具类和Proxy类2)Hibernate和JDBC混合使用的注意事项由于Hibernate的一级缓存,在通过save、update、delete等操作数据时,SQL是并没有真正发送到数据库,只有在调用flush()时,Hibernate才会将一级缓存中的状态变化同步到数据库。Hibernate的事务管理在提交事务时,会自动调用flush()操作将一级缓存同步到数据库中,才会生成SQL语句发送到数据库中。正是由于以上原因,当JDBC和Hibernate结合使用时,可能会出现丢失更新的问题。当Hibernate和JDBC结合使用时,JDBC操作不会同步到Hibernate的缓存(一级缓存和二级缓存),Hibernate缓存中的状态变化不会被JDBC感知。所以在搅拌的时候一定要特别注意这一点。由于混合数据访问技术方案的事务同步和缓存不同步,最好使用Hibernate完成读写操作,使用SpringJDBC完成读操作。比如使用SpringJDBC查询简表,使用Hibernate维护查询到的数据。如果真的要同时使用Hibernate和SpringJDBC读写数据,必须充分考虑Hibernate缓存机制带来的问题:必须充分分析数据维护逻辑,调用的flush()方法Hibernate根据需要及时避免覆盖SpringJDBC的变化,在SpringJDBC改变数据库时维护Hibernate的缓存。3、Spring的事务增强限制由于Spring事务管理是基于接口代理或动态字节码技术,事务增强是通过AOP来实现的。对于基于接口动态代理的AOP事务增强,由于接口的方法是public的,这就要求实现类的实现方法必须是public的(不能是protected、private等),不能使用static修饰符。因此,唯一可以实现接口动态代理的方法是使用“public”或“publicfinal”修饰符的方法。其他方法无法动态代理,相应的也无法实现AOP增强,即无法进行Spring事务增强。向上。基于CGLib字节码动态代理的方案通过扩展增强类和动态创建子类实现AOP增强植入。由于使用final、static和private修饰符的方法不能被子类覆盖,相应地,这些方法将不会被实现的AOP增强。因此,在使用这些修饰符时必须特别注意,以免不小心漏过事务管理的网。4、Spring事务管理的异常捕获、事务回滚Spring的事务管理器只对未检查异常进行异常回滚,Error和RuntimeException及其子类都是未检查异常。其他异常是已检查的异常。如果在服务层,使用Try和catch来捕获异常,导致服务层的异常被“拦截”,无法抛给事务管理器,给事务管理器造成一种错觉,就好像程序运行没有任何问题。因此不会有runtimeException的回滚操作。原文链接:http://my.oschina.net/lujianing/blog/305903