Maison >php教程 >PHP开发 >Spring事务抽象

Spring事务抽象

高洛峰
高洛峰original
2016-11-22 15:17:071245parcourir

1    介绍Spring框架事务管理

全面的事务支持是使用Spring框架令人信服的原因。Spring框架为事务管理提供一致性抽象,拥有以下好处:

一致性编程模型跨不同事务APIs,例如,Java事务API(JTA)、JDBC、Hibernate、Java持久化API(JPA)和Java数据对象(JDO)。

支持声明式事务管理。

比复杂事务APIs(例如,JTA)更简单。

完美的与Spring的数据访问抽象集成。

2    Spring框架的事务支持模型的优点

传统上,Java EE开发人员有两种可选的事务管理方式:全局或本地事务,都有各自的局限性。

2.1    全局事务

全局事务让你能使用多个事务资源,通常是关系型数据库和消息队列。应用程序通过JTA管理全局事务,这是一个笨重的API。而且,JTA UserTransaction通常需要来自JNDI,意味着你需要使用JNDI。显然,使用全局事务会限制应用程序代码的重用,因为JTA通常只在应用程序服务器环境有效。

以往,使用全局事务的首选方式是通过EJB CMT(容器管理事务):CMT是声明式事务管理。EJB CMT清除了相关事务的JNDI查找,不过EJB自己需要使用JNDI。它清除大多数(但不是所有)需要编写Java代码控制事务。重要的缺点是,CMT捆绑JTA和应用程序服务器环境。同时,它只有使用EJBs实现业务逻辑,或至少在事务EJB门面中时才有效。

2.2    本地事务

本地事务时特定资源,例如,事务关联JDBC连接。本地事务易于使用,但有明显缺点:它们不能跨多个事务资源。例如,使用JDBC连接管理事务不能运行在全局JTA事务中。因为应用程序服务器没有负责事务管理,它不保证跨资源的正确性。另一个缺点是本地事务时入侵式编程模型。

2.3    Spring框架的一致性编程模型

Spring解决了全局和本地事务的缺点。它让开发人员在任意环境使用一致性编程模型。开发人员只需编写自己的代码,它能抽离不同环境中的不同的事务管理。Spring框架提供声明式和编程式事务管理。大多数用户喜欢声明式事务管理,这也是推荐的。

使用编程式事务管理,开发人员使用Spring框架事务抽象,能运行在任意底层事务之上。使用首选的声明式模型,开发人员通常写很少或不写事务管理代码,因此,不依赖于Spring框架事务API,或其它事务API。

3    理解Spring框架事务抽象

Spring事务抽象的关键是事务策略的概念。事务策略通过org.springframework.transaction.PlatformTransactionManager接口定义:

public interface PlatformTransactionManager {

 

    TransactionStatus getTransaction(

            TransactionDefinition definition) throws TransactionException;

 

    void commit(TransactionStatus status) throws TransactionException;

 

    void rollback(TransactionStatus status) throws TransactionException;

}

这主要是服务提供接口(SPI),尽管它能在你的代码中使用编程模型。因为PlatformTransactionManager是一个接口,它易于模拟(mocked)和存根(stubbed)。它不需要像JNDI一样查找策略。PlatformTransactionManager实现像Spring IoC容器中定义的其它对象一样。这个好处让Spring框架事务值得抽象,甚至是使用JTA时。事务代码比直接使用JTA易于测试。

PlatformTransactionManager接口方法抛出的TransactionException是未检测异常(即,继承java.lang.RuntimeException)。事务失败是致命性的。很少情况,应用程序代码能实际从事务失败中恢复,应用程序开发人员能选择捕获并处理TransactionException。优点在于开发人员不必强制这样做。

getTransaction(..)方法依赖于TransactionDefinition参数返回TransactionStatus对象。返回的TransactionStatus可以代表一个新事务,或如果匹配事务在当前调用栈中存在则代表已存在的事务。其实就是后面这种情况,正如Java EE事务上下文,TransactionStatus关联可执行的事务。

TransactionDefinition接口说明:

Isolation(事务隔离级别):事务隔离级别。例如,该事务是否能读取其它的未提交事务?

Propagation(事务传播):通常,所有代码执行在事务范围将运行在事务中。然而,你有一个选项可以指定当事务上下文已存在时事务方法执行的行为。例如,代码能继续运行在已存在的事务中(通常情况);或已存在的事务暂停,创建一个新事务。

事务超时:该事务运行多长时间后失效,并通过底层事务自动回滚。

只读状态:当你的代码读取但不修改数据时,可以使用只读事务。在某些情况下,只读事务有利于优化,例如,当你使用Hibernate时。

这些设置反应标准事务概念。底层这些概念是使用Spring框架或任意事务管理解决方案不可或缺的。

TransactionStatus接口为事务代码提供简单的方式控制可执行事务和查询事务状态:

public interface TransactionStatus extends SavepointManager {

 

    boolean isNewTransaction();

 

    boolean hasSavepoint();

 

    void setRollbackOnly();

 

    boolean isRollbackOnly();

 

    void flush();

 

    boolean isCompleted();

 

}

 无论你是否选择Spring中的声明式或编程式事务管理,定义正确的PlatformTransactionManager实现是必不可少的。你通常通过依赖注入定义该实现。

PlatformTransactionManager通常需要知道工作的环境:JDBC、JTA、Hibernate等等。下面是定义本地PlatformTransactionManager的例子。

定义JDBC DataSource:

destroy-method="close">

    

    

    

   

PlatformTransactionManager定义引用DataSource定义:

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

   

 

如果你在Java EE容器中使用JTA,那么你可以通过使用JNDI和Spring的JtaTransactionManager获取容器的DataSource:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:jee="http://www.springframework.org/schema/jee"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/jee

http://www.springframework.org/schema/jee/spring-jee.xsd">

 

   

   

    

JtaTransactionManager不需要知道DataSource,或任意特定资源,因为它使用容器的全局事务管理。

你也能使用Hibernate的本地事务。在这种情况下,你需要定义Hibernate的LocalSessionFactoryBean,你的应用程序代码将使用它获取Hibernate Session实例。

这种情况下,txManager bean是HibernateTransactionManager。正如DataSourceTransactionManager需要引用DataSource一样,HibernateTransactionManager需要引用SessionFactory:

    

    

        

            org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml

        

    

    

        

            hibernate.dialect=${hibernate.dialect}

        

    

   

 如果你使用Hibernate和Java EE容器管理JTA事务,那么你只用使用JtaTransactionManager:

在所有这些情况下,应用程序代码不需要改变。你仅仅需要改变配置来改变如何管理事务。

4    使用事务同步资源

现在应该清楚如何创建不同的事务管理器,和它们如何链接需要同步事务的相关资源(例如,DataSourceTransactionManager链接DataSource,HibernateTransactionManager链接SessionFactory等等)。

4.1    高级同步方式

首选方式是使用Spring的高级模板基于持久化集成APIs或使用带有事务的本地ORM APIs——感知工厂bean或代理管理本地资源工厂。这些事务感知解决方案内部处理资源创建、重用、清理、资源事务同步选项和异常映射。因此,数据访问代码没有处理这些任务,但可以关注非样板式持久化逻辑。通常,你使用本地ORM API或通过使用JdbcTemplate获取模板。

4.2    低级同步方式

例如,DataSourceUtils(JDBC)、EntityManagerFactoryUtils(JPA)、SessionFactoryUtils(Hibernate)、PersistenceManagerFactoryUtils(JDO)存在底层同步方式。当你想应用程序代码直接处理本地持久化APIs,你使用这些类确保获取Spring框架管理的实例,事务是(可选)同步的,发生在进程中的异常正确映射一致性API。

例如,在JDBC的情况下,传统的JDBC方式在DataSource上调用getConnection()方法,你可以使用Spring的org.springframework.jdbc.datasource.DataSourceUtils:

Connection conn = DataSourceUtils.getConnection(dataSource);

如果已存在的事务已经有一个连接同步(链接)它,实例被返回。否则,方法调用触发器创建新的连接,(可选)同步任意已存在的事务,后续在相同事务中重用。

这种方式不需要Spring事务管理(事务同步是可选的),因此,无论你是否使用Spring管理事务你都能使用它。

当然,一旦你使用Spring的JDBC支持、JPA支持或Hibernate支持,你通常不喜欢使用DataSourceUtils或其它的帮助类。

4.3    TransactionAwareDataSourceProxy

在底层,已经存在TransactionAwareDataSourceProxy类。这是目标DataSource代理,包装目标DataSource到感知Spring管理事务。为此,它类似于Java EE服务器提供的传统JNDI DataSource。

5    声明式事务管理

Spring框架的声明式事务管理让Spring面向切面编程(AOP)成为可能,尽管,Spring框架发布包以模板形式自带事务切面代码,一般需要理解AOP。

5.1    理解Spring框架的声明式事务实现

通过元数据驱动事务通知(当前基于XML或注解)。AOP联合事务元数据产生AOP代理,使用TransactionInterceptor联合适当的PlatformTransactionManager实现驱动事务环绕方法调用。

QQ图片20161122092324.png

5.2    声明式事务实现的例子

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

    

    

        

            

            

            

            

        

    

 

    

        

        

    

 

   

        

        

        

        

    

 

   

        

    

 5.3    回滚声明式事务

默认情况下,只有抛出运行时异常或Error时导致Spring事务回滚。检查异常不导致Spring事务回滚。

你能明确配置事务回滚的异常类型。

    

        

        

    

你也能指定事务不会滚的异常类型:

    

       

        

   

5.4    为不同的bean配置不同的事务语义

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

 

    

        

        

        

        

    

 

    

        

            

            

        

    

 

    

        

            

        

    

5.5    设置

默认设置:

事务传播设置是REQUIRED

事务隔离级别是DEFAULT

事务是读/写

事务超时时间默认为底层事务系统的超时时间,或如果不支持超时就没有

任意RuntimeException异常触发回滚,任意检查Exception不触发

你能改变这些默认设置;嵌套在标签中的标签的各种属性:

QQ图片20161122092324.png

5.6    使用@Transactional

开启注解管理事务:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

 

   

   

   

        

    

 

    

Service声明注解:

@Transactional

public class DefaultFooService implements FooService {

 

    Foo getFoo(String fooName);

 

    Foo getFoo(String fooName, String barName);

 

    void insertFoo(Foo foo);

 

    void updateFoo(Foo foo);

}

@Transactional设置:

QQ图片20161122092324.png

@Transactional的多事务管理器:

public class TransactionalService {

 

    @Transactional("order")

    public void setSomething(String name) { ... }

 

    @Transactional("account")

    public void doSomething() { ... }

}

 

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

    ...

    

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

    ...

    

自定义简称注解

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Transactional("order")

public @interface OrderTx {

}

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Transactional("account")

public @interface AccountTx {

}

5.7    事务传播

PROPAGATION_REQUIRED

QQ图片20161122092324.png

当设置事务传播为PROPAGATION_REQUIRED时,为每个方法创建一个逻辑事务范围。每个逻辑事务范围能单独决定只回滚状态,外部事务范围逻辑独立于内部事务范围。当然,在标准的PROPAGATION_REQUIRED情况下,所有这些范围将映射相同的物理事务。因此,只回滚标记设置在内部事务范围不影响外部事务发生的实际提交。

然而,在这种情况下,内部事务范围设置为只回滚标记,外部事务没有决定回滚,因此回滚是意想不到的。相应的UnexpectedRollbackException抛出。

PROPAGATION_REQUIRES_NEW

QQ图片20161122092324.png

对比PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW使用完全独立的事务。在这种情况下,底层物理事务是不同的,因此,能独自提交或回滚,外部事务的回滚状态不受内部事务的影响。

PROPAGATION_NESTED

使用一个能回滚到多个保存点的物理事务。这种回滚允许内部事务范围触发它的范围回滚,外部事务能够继续物理事务,尽管一些操作已经回滚。该设置通常映射到JDBC保存点,因此,只能用于JDBC资源事务。

6    编程式事务管理

Spring框架提供两种编程式事务管理:

使用TransactionTemplate(推荐)

使用PlatformTransactionManager直接实现

6.1    使用TransactionTemplate

TransactionTemplate采用与其它Spring模板(例如,JdbcTemplate)相同的方式。

public class SimpleService implements Service {

 

    private final TransactionTemplate transactionTemplate;

 

    public SimpleService(PlatformTransactionManager transactionManager) {

        Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");

        this.transactionTemplate = new TransactionTemplate(transactionManager);

    }

 

    public Object someServiceMethod() {

        return transactionTemplate.execute(new TransactionCallback() {

            public Object doInTransaction(TransactionStatus status) {

                updateOperation1();

                return resultOfUpdateOperation2();

            }

        });

    }

}

如果没有返回值,则使用TransactionCallbackWithoutResult类:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

    protected void doInTransactionWithoutResult(TransactionStatus status) {

        updateOperation1();

        updateOperation2();

    }

});

在业务代码中回滚:

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

 

    protected void doInTransactionWithoutResult(TransactionStatus status) {

        try {

            updateOperation1();

            updateOperation2();

        } catch (SomeBusinessExeption ex) {

            status.setRollbackOnly();

        }

    }

});

指定事务设置:

public class SimpleService implements Service {

 

    private final TransactionTemplate transactionTemplate;

 

    public SimpleService(PlatformTransactionManager transactionManager) {

        Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");

        this.transactionTemplate = new TransactionTemplate(transactionManager);

        this.transactionTemplate.setIsolationLevel(

        TransactionDefinition.ISOLATION_READ_UNCOMMITTED);

        this.transactionTemplate.setTimeout(30); // 30秒

   }

}

使用Spring XML配置TransactionTemplate:

class="org.springframework.transaction.support.TransactionTemplate">

   

   

6.2    使用PlatformTransactionManager

DefaultTransactionDefinition def = new DefaultTransactionDefinition();

def.setName("SomeTxName");

def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);

try {

    // 执行业务逻辑

} catch (MyException ex) {

    txManager.rollback(status);

    throw ex;

}

txManager.commit(status);

7    选择编程式还是声明式管理事务?

如果你只有少量事务操作选择编程式事务管理。

8    事务绑定事件

从Spring 4.2开始,监听器事件可以绑定到事务阶段。

@Componentpublic class MyComponent {

 

    @TransactionalEventListener

    public void handleOrderCreatedEvent(CreationEvent creationEvent) {

          ...

    }

 

}

TransactionalEventListener注解暴露phase属性允许定制监听事务阶段。有效的阶段是:BEFORE_COMMIT、AFTER_COMMIT(默认)、AFTER_ROLLBACK和AFTER_COMPLETION。 


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn