Spring 事务管理

事务是信息化系统的最基本功能,基本的CRUD 操作没有任何逻辑意义。一次业务逻辑操作往往需要具有原子性:典型的两个账户转账的情形,必然涉及两次数据库操作,这两次操作必须处于事务中,而不能只进行一半。

Hibernate 建议所有的数据库访问都应放在事务内进行,即使只进行只读操作。事务又应该尽可能短,因为长事务会导致长时间无法释放表内行级锁,从而降低系统并发的性能。Spring 同时支持编程式事务和声明式事务,尽量考虑使用声明式事务,因为声明式事务管理可分离业务逻辑和事务管理逻辑,具备良好的适应性。

一、编程式的事务管理

另外,编程式事务提供了TransactionTemplate 模板类,该类可以大大减少事务操作的代码。因此TransactionTemplate 采用Callback 避免让开发者重复书写其打开事务、提交事务及回滚事务等代码,同时TransactionTemplate 无须书写大量的t可...catch 块。

HibernateTemplate 必须提供PlatformTransactionManager 实例。该实例既可以在代码中手动设置,也可以使用Spring 的依赖注入。总之,只要获取了PlatformTransactionManager 引用, TransactionTemplate 就可以完成事务操作。

使用TransactionTemplate 不需要显式地开始事务,甚至不需要显式地提交事务。这些步骤都由模板完成。但出现异常时,应通过TransactionStatus 的setRollbackOnly 显式回攘事务。

TransactionTemplate 的execute 方法接收一个TransactionCallback 实例。Callback 也是Spring 的经典设计,用于简化用户操作, TransactionCallback 包含如下方法。

    Object dolnTransaction(TransactionStatus status)

该方法的方法体就是事务的执行体。如果事务的执行体没有返回值,则可以使用TransactionCallbackWithoutResult 类的实例。这是个抽象类,不能直接实例化,只能用于创建匿名内部类。它也是TransactionCallback 接口的子接口,该抽象类包含一个抽象方法:

void dolnTransactionWithoutResult(TransactionStatus status)

该方法与dolnTransaction 的效果非常相似,区别在于该方法没有返回值,即事务执行体无须返回值。

在下面的示例中, PlatformTransactionManager 实例采用适用于Hibernate 的事务管理器来实现类HibernateTransactionManager,该实现类是个局部事务管理器,容器中仅仅部署了该事务管理器bean ,因此应在代码中手动为TransactionTemplate 注入事务管理器bean。

下面是Hibernate 局部事务管理的配置文件的源代码:

<?xmlversion="1.0"encoding="gb2312"?>

<!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!--定义数据源-->

    <beanid="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <propertyname="driverClassName">

<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>

</property>

        <propertyname="url">

<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=HibernateDB</value>

</property>

        <propertyname="username"value="sa"/>

        <propertyname="password"value="sa"/>

    </bean>

   

    <!--定义一个hibernate的SessionFactory-->

<beanid="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <propertyname="dataSource"><reflocal="dataSource"/></property>

        <propertyname="mappingResources">

            <list>

                <!--以下用来列出所有的PO映射文件-->

                <value>

tansaction/TransactionProxyFactoryBean/Person.hbm.xml

</value>

            </list>

        </property>

        <propertyname="hibernateProperties">

            <props>

                <!—

此处用来定义hibernate的SessionFactory的属性,

不同数据库连接,启动时选择create,update,create-drop

-->

                <propkey="hibernate.dialect">

org.hibernate.dialect.SQLServerDialect

</prop>

                <propkey="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

   

    <!—

定义事务管理器,适用于Hibernte的事务管理器

使用HibernateTransactionManager类,该类是PlatformTransactionManager 接口针对

采用Hibernate持久化连接的特定实现

 -->

<beanid="transactionManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <!--  HibernateTransactionManagerbean需要依赖注入一个SessionFactorybean的引用-->

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

</beans>

下面是采用TransactionTemplate和HibemateTemplate的事务操作代码:

public class TransactionTest{

public static void main(String[] args){

//因为并未在web 应用中测试,故需要手动创建Spring 的上下文

final ApplicationContext ctx = new FileSystemXrnlApplicationContext("bean.xml");

//获得Spring 上下文的事务管理器

PlatformTransactionManager transactionManager =

(PlatformTransactionManager)ctx.getBean("transactionManager");

final SessionFactory sessionFactory = (SessionFactory)ctx.getBean("sessionFactory");

//以事务管理器实例为参数,创建TransactionTemplate对象

TransactionTemplate tt = new TransactionTemplate(transactionManager);

//设置TransactionTemplate的事务传播属性

tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUlRED);

//执行TransactionTemplate的execute方法,该方法需要TransactionCallback实例

tt.execute(new TransactionCallbackW工thoutResult(){

//采用TransactionCallbackWithoutResult匿名内部类的形式执行

protectedvoid dolnTransactionWithoutResult(TransactionStatus ts){

try{

//以SessionFactory 实例为参数创建HibernateTemplate

HibernateTemplate hibernateTemplate = new HibernateTemplate(sessionFactory);

MyTestpl = new MyTest ("Jack");

//保存第一个实例

hibernateTemplate.save(pl);

//让下面的数据库操作抛出异常即可看出事务效果。前面的操作也不会生效

MyTestp2 = new MyTest ("Jack");

//保存第二个实例,可将Person 的name 属性设为标识属性,并引起主键重复的异常,

//可看出前一条记录也不会加入数据库中

hibernateTemplate.save(p2);

}catch (Exception e){

ts.setRollbackOnly();

            }

            }

        });

    }

}

查看数据库的mytable 表,该表中没有任何记录(如果没有采用事务,第一条记录应该可以进去。而两次保存记录放在doInTransactionWithoutResult 方法中执行) ,因为该方法的方法体具有事务性,该方法的数据库操作要么全部生效,要么全部失效。由于第二条记录违反了数据库的主键约束,因此,记录全部失效。

 

二、声明式事务管理

通常建议采用声明式事务管理。声明式事务管理的优势非常明显,代码中无须关注事务逻辑,由Spring 声明式事务管理负责事务逻辑:声明式事务管理无须与具体的事务逻辑糯合,可以方便地在不同事务逻辑之间切换。

声明式事务管理的配置方式通常有如下四种。

1)   使用TransactionProxyFactoryBean 为目标bean 生成事务代理的配置。此方式最传统,但配置文件膝肿,难以阅读。

2)   采用bean 继承的事务代理配置方式比较简沽,但依然是增量式配置。

3)   使用BeanNameAutoProxyCreator ,根据bean name 自动生成事务代理的方式,这是直接利用Spring 的AOP 框架配置事务代理的方式,需要对Spring 的AOP 框架有所理解,但这种方式避免了增量式配置,效果非常不错。

4)   DefaultAdvisorAutoProxyCreator: 这也是直接利用Spring 的AOP 框架配置事务代理的方式,效果也非常不错,只是这种配置方式的可读性不如第三种方式。

1. 利用TransactionProxyFactoryBean 生成事务代理

采用这种方式的配置时,其配置文件的增加非常快,每个bean需要配置一个目标,另外还需要使用TransactionProxyFactoryBean 配置一个代理bean。

这是一种最原始的配置方式,下面是使用TransactionProxyFactoryBean 的配置文件:

<?xmlversion="1.0"encoding="gb2312"?>

<!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!--定义数据源-->

    <beanid="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <propertyname="driverClassName">

<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>

</property>

        <propertyname="url">

<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=HibernateDB</value>

</property>

        <propertyname="username"value="sa"/>

        <propertyname="password"value="sa"/>

    </bean>

   

    <!--定义一个hibernate的SessionFactory-->

<beanid="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <propertyname="dataSource"><reflocal="dataSource"/></property>

        <propertyname="mappingResources">

            <list>

                <!--以下用来列出所有的PO映射文件-->

                <value>

tansaction/TransactionProxyFactoryBean/Person.hbm.xml

</value>

            </list>

        </property>

        <propertyname="hibernateProperties">

            <props>

                <!—

此处用来定义hibernate的SessionFactory的属性,

不同数据库连接,启动时选择create,update,create-drop

-->

                <propkey="hibernate.dialect">

org.hibernate.dialect.SQLServerDialect

</prop>

                <propkey="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

   

    <!--定义事务管理器,适用于Hibernte的事务管理器-->

<beanid="transactionManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <!--  HibernateTransactionManagerbean需要依赖注入一个SessionFactorybean的引用-->

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

 

    <!--定义DAOBean,作为事务代理的目标-->

    <beanid="personDao"

 class="tansaction.TransactionProxyFactoryBean.PersonDaoHibernate">

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

 

    <!--定义DAObean的事务代理-->

<beanid="personDaoProxy"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

        <propertyname="transactionManager">

<refbean="transactionManager"/>

</property>

        <propertyname="transactionAttributes">

            <props>

                <propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <propkey="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

        <propertyname="target">

            <reflocal="personDao"/>

        </property>

    </bean>

</beans>

在上面的配置文件中,personDao 需要配置两个部分:一个是personDao 的目标bean ,该目标bean 是实际DAO bean,以实际的DAO bean 为目标,建立事务代理;另一个是组件,需要一个目标bean 和一个事务代理来组成。

但这种配置方式还有一个缺点:目标bean 直接暴露在Spring 容器中,可以直接引用,如果目标bean 被误引用,将导致业务操作不具备事务性。

为了避免这种现象,可将目标bean 配置成嵌套bean,下面是目标bean 和事务代理的配置代码:

<beanid="personDaoProxy"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

        <propertyname="transactionManager">

<refbean="transactionManager"/>

</property>

        <propertyname="transactionAttributes">

            <props>

                <propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <propkey="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

        <propertyname="target">

            <!--采用嵌套bean配置目标bean-->

            <beanclass="tansaction.TransactionProxyFactoryBean.

PersonDaoHibernate">

                <propertyname="sessionFactory">

<reflocal="sessionFactory"/>

</property>

            </bean>

        </property>

</bean>

 

2. 利用继承简化配置

大部分情况下,每个事务代理的事务属性大同小异,事务代理的实现类都是TransactionProxyFactoryBean,事务代理bean 都必须注入事务管理器。对于这种情况, Spring 提供了bean 与bean 之间的继承,可以简化配置。将大部分通用的配置,配置成事务模板,而实际的事务代理bean,则继承事务模板。这种配置方式可以减少部分配置代码。下面是采用继承的配置文件:

<!--配置事务模板,模板bean被设置成abstractbean.保证不会被初始化-->

<beanid="proxyTemplate"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"

        lazy-init="true"abstract="true">

        <!--为事务模板注入事务管理器-->

        <propertyname="transactionManager">

<refbean="transactionManager"/>

</property>

        <!--设置事务属性-->

        <propertyname="transactionAttributes">

            <props>

                <propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <propkey="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

 

    <!--实际的事务代理bean,继承事务模板配置-->

    <beanid="personDaoProxy"parent="proxyTemplate">

        <propertyname="target">

            <!--采用嵌套bean配置目标bean-->

            <beanclass="tansaction.TransactionProxyFactoryBean.

PersonDaoHibernate">

                <propertyname="sessionFactory">

<reflocal="sessionFactory"/>

</property>

            </bean>

        </property>

    </bean>

相比前面直接采用TransactionProxyFactoryBean 的事务代理配置方式,这种配置方式可以大大减少配置文件的代码量。每个事务代理的配置都继承事务模板,无须重复指定事务代理的实现类,也无须重复指定事务传播属性。但如果新的事务代理有额外的事务属性,也可指定自己的事务属性,此时,子bean 的属性覆盖父bean 的属性。当然每个事务代理bean 都必须配置自己的目标bean ,这是不可避免的。

从上面的配置可看出,事务代理的配置依然是增量式的,每个事务代理都需要单独配置。

 

3. 用BeanNameAutoProxyCreator 自动创建事务代理

下面介绍-种优秀的事务代理配置策略,采用这种配置策略,完全可以避免增量式配置,使所有的事务代理由系统自动创建。由于容器中的目标bean 自动消失,可避免需要使用嵌套bean 来保证目标bean 不可被访问。

这种配置方式依赖于Spring 提供的bean 后处理器,该后处理器用于为每个bean 自动创建代理,此处的代理不仅可以是事务代理,也可以是任意的代理,只需要有合适的拦截器即可。这些是AOP 框架的概念,笔者在此处不对AOP 进行深入介绍。读者只需了解这种事务代理的配置方式即可。

下面是采用BeanNameAutoProxyCreator 配置事务代理的配置文件:

<?xmlversion="1.0"encoding="gb2312"?>

<!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!--定义数据源-->

    <beanid="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <propertyname="driverClassName">

<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>

</property>

        <propertyname="url">

<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=HibernateDB</value>

</property>

        <propertyname="username"value="sa"/>

        <propertyname="password"value="sa"/>

    </bean>

   

    <!--定义一个hibernate的SessionFactory-->

    <beanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <propertyname="dataSource"><reflocal="dataSource"/></property>

        <propertyname="mappingResources">

            <list>

                <!--以下用来列出所有的PO映射文件-->

                <value>tansaction/BeanNameAutoProxyCreator/Person.hbm.xml</value>

            </list>

        </property>

        <propertyname="hibernateProperties">

            <props>

                <!—

此处用来定义hibernate的SessionFactory的属性,

不同数据库连接,启动时选择create,update,create-drop

-->

                <propkey="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>

                <propkey="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

   

    <!--定义事务管理器,适用于Hibernte的事务管理器-->

<beanid="transactionManager"

        class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <!--  HibernateTransactionManagerbean需要依赖注入一个SessionFactorybean的引用-->

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

   

    <!--  配置事务拦截器-->

<beanid="transactionInterceptor"

 class="org.springframework.transaction.interceptor.TransactionInterceptor">

        <!--  事务拦截器bean需要依赖注入一个事务管理器-->

        <propertyname="transactionManager"ref="transactionManager"/>

        <propertyname="transactionAttributes">

            <!--  下面定义事务传播属性-->

            <props>

                <propkey="insert*">PROPAGATION_REQUIRED</prop>

                <propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <propkey="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

 

    <!--定义BeanNameAutoProxyCreator,该bean是个bean后处理器,无需被引用,因此没有id属性-->

    <beanclass="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

        <!--指定对满足哪些beanname的bean自动生成业务代理-->

        <propertyname="beanNames">

            <!--下面是所有需要自动创建事务代理的bean-->

            <list>

                <value>personDao</value>

            </list>

            <!--此处可增加其他需要自动创建事务代理的bean-->

        </property>

        <!--  下面定义BeanNameAutoProxyCreator所需的事务拦截器-->

        <propertyname="interceptorNames">

            <list>

                <value>transactionInterceptor</value>

                <!--此处可增加其他新的Interceptor-->

            </list>

        </property>

    </bean>

   

    <!--定义DAOBean,作为事务代理的目标-->

    <beanid="personDao"class="tansaction.BeanNameAutoProxyCreator.PersonDaoHibernate">

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

</beans>

TranscationInterceptor是一个事务拦截器bean,需要传入一个TransactionManager的引用。配置中使用Spring 依赖注入该属性,事务拦截器的事务属性通过transactionAttributes 来指定,该属性有props 子元素,并在配置文件中定义了三个事务传播规则。所有以insert 开始的方法,都采用PROPAGATION_REQUIRED的事务传播规则。

当程序抛出MyException异常及其子异常时,会自动回滚事务:所有以find 开头的方法,都采用PROPAGATION_REQUIRED事务传播规则,并且具有只读性;其他方法则采用PROPAGATION_REQUIRED的事务传播规则。

BeanNameAutoProxyCreator是根据bean 名生成自动代理的代理创建器,该bean 通常需要接受两个参数:第一个是beanNames 属性,该属性用来设置哪些bean 需要自动生成代理,另一个属性是interceptorNames,该属性则指定事务拦截器,在自动创建事务代理时,系统会根据这些事务拦截器的属性来生成对应的事务代理。

为了让读者对这种配置方式有信息,可对PersonDaoHibemate的save 方法进行简单修改,修改后的save 方法如下:

public void save(Person person){

getHibernateTemplate() .save(person);

//下面两行代码没有实际意义,仅仅为了引发数据库异常

DataSource ds = null;

DataSourceUtils.getConnection(ds);

}

在主程序中调用该save 方法,主程序调用save 方法的代码如下:

for (int i = 0 ; i < 10  ; i++ ){

    //保存Person 实例

    personDao.save(new Person(String.valueOf(i) , i + 10));

}

执行完主程序的该片段后,数据库不会插入任何记录

这种配置方式相当简洁,每次都增加了新的bean。如果需要该bean 的方法具有事务性,只需在BeanNameAutoProxyCreator 的beanNames 属性下作相应修改。该行告诉bean 后处理需要为哪个bean 生成事务代理。

 

4. 用DefaultAdvisorAutoProxyCreator自动创建事务代理

这种配置方式与BeanNameAutoProxyCreator 自动创建代理的方式非常相似,都是使用bean 后处理器为目标bean 创建实物代理。区别是前者使用事务拦截器创建代理;后者需要使用Advisor 创建事务代理。

事实上,采用DefaultAdvisorAutoProxyCreator 的事务代理配置方式更加简洁,因为这个代理生成器自动搜索Spring 容器中的Advisor,并为容器中所有的bean 创建代理。相对前一种方式,这种方式的可读性不如前一种直观,笔者还是推荐采用第三种配置方式,下面是使用DefaultAdvisorAutoProxyCreator的配置方式:

<?xmlversion="1.0"encoding="gb2312"?>

<!DOCTYPEbeansPUBLIC"-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <!--定义数据源-->

    <beanid="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <propertyname="driverClassName">

<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>

</property>

        <propertyname="url">

<value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=HibernateDB</value>

</property>

        <propertyname="username"value="sa"/>

        <propertyname="password"value="sa"/>

    </bean>

   

    <!--定义一个hibernate的SessionFactory-->

    <beanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <propertyname="dataSource"><reflocal="dataSource"/></property>

        <propertyname="mappingResources">

            <list>

                <!--以下用来列出所有的PO映射文件-->

                <value>tansaction/DefaultAdvisorAutoProxyCreator/Person.hbm.xml</value>

            </list>

        </property>

        <propertyname="hibernateProperties">

            <props>

                <!—

此处用来定义hibernate的SessionFactory的属性,

不同数据库连接,启动时选择create,update,create-drop

-->

                <propkey="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>

                <propkey="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

   

    <!--定义事务管理器,适用于Hibernte的事务管理器-->

<beanid="transactionManager"

        class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <!--  HibernateTransactionManagerbean需要依赖注入一个SessionFactorybean的引用-->

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

   

    <!--  配置事务拦截器-->

<beanid="transactionInterceptor"

        class="org.springframework.transaction.interceptor.TransactionInterceptor">

        <!--  事务拦截器bean需要依赖注入一个事务管理器-->

        <propertyname="transactionManager"ref="transactionManager"/>

        <propertyname="transactionAttributes">

            <!--  下面定义事务传播属性-->

            <props>

                <propkey="insert*">PROPAGATION_REQUIRED</prop>

                <propkey="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <propkey="*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

       

    <!--  定义事务Advisor-->

    <beanclass="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">

        <propertyname="transactionInterceptor"ref="transactionInterceptor"/>

    </bean>

   

    <!--DefaultAdvisorAutoProxyCreator搜索容器中的advisor,并为每个bean创建代理-->

    <beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

   

    <!--定义DAOBean,由DefaultAdvisorAutoProxyCreator自动生成事务代理-->

    <beanid="personDao"class="tansaction.DefaultAdvisorAutoProxyCreator.PersonDaoHibernate">

        <propertyname="sessionFactory"><reflocal="sessionFactory"/></property>

    </bean>

</beans>

在这种配置方式下,配置文件变得更加简沽,增加目标bean 时不需要增加任何额外的代码,容器会自动为目标bean 生成代理。但这种方式的可读性相对较差。

 

5. 事务策略的思考

Spring 的事务管理都是通过PlatformTransactionManager 完成。在Hibernate应用中,PlatformTransactionManager 可能是Hibernat SessionFactory ,也可能是JtaTransactionManager。前者是采用局部事务管理的实现,后者是采用基于JTA 支持的全局事务管理的实现。

采用声明式事务管理,可以使应用的事务策略方便地在不同的事务策略之间切换。使程序的代码可以更加专注于业务逻辑的实现,而无须理会事务逻辑。因此,即使应用运行于支持JTA 事务的应用服务器环境,也应考虑使用Spring 的声明式事务管理。假如需要改变服务器,而新的服务器无法提供JTA 事务支持,声明式事务可以方便地切换成局部事务(只需对配置文件简单修改)。

 

你可能感兴趣的:(Spring 事务管理)