欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SPRING事务实现

程序员文章站 2022-05-03 07:56:58
...

事务传播行为种类

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,

它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:

表1事务传播行为类型

事务传播行为类型

说明

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

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

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

 

ibatis spring 事务实现
1.配置动态数据源-根据KEY路由

<bean id="dataSource" class="com.xiao.AbstractRoutingDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="db2" value-ref="ds-db2" />
                <entry key="sqlserver" value-ref="ds-sql" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="ds-db2" />
    </bean>  

  
2.配置事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="insert*" propagation="REQUIRED" rollback-for="Throwable" />
            <tx:method name="delete*" propagation="REQUIRED" rollback-for="Throwable" />
            <tx:method name="update*" propagation="REQUIRED" rollback-for="Throwable" />
            <tx:method name="toggle*" propagation="REQUIRED" rollback-for="Throwable" />
            <tx:method name="query*" read-only="true" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="servicePointcut" expression="execution(* com.xiao..*ServiceImpl.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut" />
        <!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="otherPointcut" /> -->
</aop:config>

 
3.配置IBATIS

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocations">
            <list>
                <value>classpath:/com/xiao/sqlmap-config.xml</value>
               
            </list>
        </property>
        <property name="dataSource" ref="dataSource" />
        <property name="lobHandler" ref="lobHandler" />
</bean>


4.注解事务配置
XML加上  <tx:annotation-driven transaction-manager="transactionManager"/>
在实现类加上这个
@Transactional(propagation=Propagation.NOT_SUPPORTED,rollbackFor=RuntimeException.class)

问题:事务方法中,多个DAO的连接用的应该是同一个connection,SPRING是如何让sqlMapClient的connection和
transactionManager中一致的呢?
分析:可以看到它们用到的是同一个数据源。在SqlMapClientFactoryBean.afterPropertiesSet中会将datasource封装成
TransactionAwareDataSourceProxy一个代理。代理了getConnection方法。
实现细节见DataSourceUtils.doGetConnection ,该方法从当前线程中取线程本地数据(ThreadLocal)MAP,key为数据源,
值为连接,这个如果是事务中,事务的AOP会在进入事务方法前放入这个连接。所以取的连接是同一个。

问题:事务中切换动态数据源切换没用?
分析:根据上面分析,取连接时取的是进入事务方法前设置的连接,所以不行。AbstractRoutingDataSource实际是getConnection
方法动态切换数据源,但这个方法被代理了。而XML配置的DAO和事务的数据源是同一个。

事务分析:
TransactionInterceptor拦截事务方法,第一次进入,当前线程对应的ConnectionHolder为空,
调用doBegin方法,生成一个新的数据库连接ConnectionHolder,事务Transact类设置为newConnectionHolder
属性为TRUE,标记这个Transact为第一次,如果是第一次的Transact,绑定ConnectionHolder,到线程本地上下文
中,如果事务方法中还是嵌套事务情况,假设传播级别是PROPAGATION_REQUIRED(用以前的事务),进入嵌套事务方法时,连接取的
还是第一次的ConnectionHolder,但Transact类设置为newTransaction false(用来区分事务是否要提交);

Propagation.NOT_SUPPORTED:会将之前的事务挂起,当前线程的连接清空;NOT_SUPPORTED方法里的后续的SQL会直接提交,如果前后操作的SQL是同一条数据,
会引起死锁,前面的事务锁住了数据,后续事务无法提交。
Propagation.REQUIRES_NEW:会将之前的事务挂起,新建一个事务,当前线程的连接设置成新事务的数据库连接;新事务提交后,恢复之前的事务,当前线程的数据库连接
设置成恢复事务的连接。保证第一个事务的SQL用的是同一个连接。

相关标签: spring事务