`
yangzb
  • 浏览: 3472755 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

用 Spring 框架指定自定义隔离级别

阅读更多

在 Java EE 应用程序的分布式事务中使用自定义隔离级别

2006 年 11 月 20 日

如果您正在构建一个应用程序,该应用程序要求在执行用例时在全局事务中具有 自定义隔离级别,您可能已经发现这是一件困难的事,因为 Java™ Transaction API 并不提供对自定义隔离级别的支持。幸运地是,Spring 框架允许您设计在全局事务中使用自定义隔离级别的 Web 和企业应用程序,但这却不是一件容易的事。在本文中,Ricardo Olivieri 用 7 个详细的步骤演示了这一过程。

许多 Java Enterprise Edition(EE)应用程序在执行用户请求时都会访问多处资源。例如,应用程序也许需要将一条消息放到一个面向消息的中间件队列中,并在相同的事务上 下文中更新数据库行。可以通过使用应用服务器提供的 Java Transaction API(JTA)事务管理器和兼容 XA 的驱动程序连接到数据资源来实现这一任务。但应用程序的需求也许会在执行一个用例时调用全局事务中的自定义隔离级别(custom isolation level) —— JTA 事务管理器并不支持自定义隔离级别。如果正在使用 Spring 框架,出这个原因,如果为 Spring 配置文件中的全局事务指定一个自定义隔离级别,将会抛出一个异常。

本文展示了一种能够 使用 Spring 来指定全局事务中的自定义隔离级别的方法。如果您部署应用程序的应用服务器,允许在定义数据源的位置指定作为数据库访问的隔离级别值,那么该方法都是有效 的。为从本文中获益,您应该熟悉 Spring 框架并理解如何在 Spring 配置文件中定义事务代理及面向方面的 advice。在对应用服务器熟悉的前提下,也假设您熟悉 Java EE 设计模式和全局/分布式事务的概念。

问题

软 件应用程序的需求也许做了这样的规定(这里的许多技术超出了本文讨论范围),即在执行一个给定用例的过程中,必须将相同的隔离级别使用到所有的数据访问 中。需求也许还这样规定,在一个用例实现中只要访问了两项或超过两项的外部资源,该应用程序就应该使用全局事务。例如,作为用例实现的一部分,应用程序也 许会查询两个不同的数据库表并将一条消息放到消息队列中。针对这个用例的设计也许需要使用 “已提交读” 隔离级别来执行两个数据库 READ 操作。但也需要在执行不同的 用例时,应用程序会使用不同的隔离级别(如 “可重复读”)来执行这两个相同数据库的 READ 操作。在这两个用例的执行中,应用程序执行相同的数据库操作和部分相同的代码段,但却必须使用不同的隔离级别。

您可以分别为两个 READ 操作定义方法,并以要使用的隔离级别作为参数。这些方法的调用者会依据执行中的用例来指定相应的隔离级别。但即使这种方法会起作用,将这种逻辑包含在 Java 代码中并不是最佳方法,且维护代码会很困难。表面上看,利用 Spring 框架的功能似乎是更好的方法。Spring 是一个强大的框架,这在很大程度上是由于其为应用程序定义事务的强大功能。Spring 让您用一种清晰的方式指定事务属性,如隔离级别、传播行为和异常处理行为(例如,当抛出特定的异常时,事务是否应该自动回滚)。但缺乏对指定自定义隔离级 别的支持是 JTA 是一块软肋,如下列场景所说明的那样。

什么是服务对象?
在本文的上下文中,可以把服务对象 想象成负责隐藏业务组件并集中工作流的门面(facade)。它们的方法为应用程序的使用场景定义了实现。服务对象为客户机(Web UI、远程服务客户机,等等)提供粗糙的界面。Jave EE Session Facade 设计模式(参见 参考资料 )很好地适应了服务对象。(在 EJB 世界中,会话的门面是由企业会话 bean 来实现的。)

Spring 场景

使用 JTA 事务管理器的新手或只对它了解一点的开发人员也许想要为服务对象(如 OrderService )(参见 什么是服务对象? )的实现定义(在 Spring 配置文件中)一个事务代理,如清单 1 所示:


清单 1. 使用 JTA 事务管理器的事务代理的错误定义

                

<bean id="transactionManager"
  class="org.springframework.transaction.jta.JtaTransactionManager">
    <constructor-arg>
        <ref local="jtaTransactionManager" />
    </constructor-arg>
</bean>

<bean id="orderService" 
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager" />
    </property>
    <property name="proxyInterfaces">
        <list>
            <value>sample.services.OrderService</value>
        </list>
    </property>
    <property name="target">
        <ref local="orderServiceTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="save*">PROPAGATION_REQUIRED, ISOLATION_SERIALIZABLE</prop>
            <prop key="delete*">PROPAGATION_REQUIRED, ISOLATION_READ_UNCOMMITTED</prop>
            <prop key="find*">PROPAGATION_REQUIRED, ISOLATION_READ_UNCOMMITTED, 
            readOnly</prop>
        </props>
    </property>
</bean>

 

清单 1 中定义了两个 bean。第一个 bean 的定义指定了应用程序将使用的事务管理器。正如您能看到的那样,这个 bean 依赖于另一个叫做 jtaTransactionManager 的 bean,而这个 bean 的定义依赖于您正在使用的应用服务器。例如,对于 IBM WebSphere Application Server 来说,这个 bean 的定义是这样的:

 
<bean id="jtaTransactionManager" 
    class="org.springframework.transaction.jta.WebSphereTransactionManagerFactoryBean" 
    singleton="true" /> 
 

 

为什么需要一个 JTA 事务管理器?
可能需要 JTA 事务管理器是因为当应用程序执行一个用例实现时要访问多处资源。例如,在数据库中保存一条记录时,代码也会将一条输出消息放到消息队列中。要在这种情况下保证数据的集成性和原子性,需要一个支持分布式事务的事务管理器。

清单 1 中第二个 bean(称为 orderService )包含一个服务对象的事务代理定义,该服务对象实现了一个名为 OrderService 的接口。这个代理为三个方法声明了三个事务性定义:save()delete()find() 。由于 “序列化” 和 “未提交读” 被指定为这些方法的隔离级别,那么期望这些就是在运行时获得的隔离级别是符合逻辑的。然而,请注意该代理定义包含了对 JTA 事务管理器的引用。如果用这个配置运行应用程序,您也许会十分惊诧。只要执行了 OrderService 实现的 save()delete()find() 方法,就会出现这样一个异常:

 
org.springframework.transaction.InvalidIsolationLevelException: 
  JtaTransactionManager does not support custom isolation levels
  at org.springframework.transaction.jta.JtaTransactionManager.applyIsolationLevel(
  JtaTransactionManager.java:617)
  at org.springframework.transaction.jta.JtaTransactionManager.doJtaBegin(
  JtaTransactionManager.java:595)
  at org.springframework.transaction.jta.JtaTransactionManager.doBegin(
  JtaTransactionManager.java:559)
  at org.springframework.transaction.support.AbstractPlatformTransactionManager.
  getTransaction(AbstractPlatformTransactionManager.java:234)
  ...

 

出现这个错误是因为 JTA 事务管理器不支持自定义隔离级别。当使用 JTA 事务管理器时,事务代理的 bean 定义会和清单 2 中的类似:


清单 2. 使用 JTA 事务管理器的事务代理的正确定义

                
<bean id="orderService" 
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager" />
    </property>
    <property name="proxyInterfaces">
        <list>
            <value>sample.services.OrderService</value>
        </list>
    </property>
    <property name="target">
        <ref local="orderServiceTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="save*">PROPAGATION_REQUIRED, ISOLATION_DEFAULT</prop>
            <prop key="delete*">PROPAGATION_REQUIRED, ISOLATION_DEFAULT</prop>
            <prop key="find*">PROPAGATION_REQUIRED, ISOLATION_DEFAULT,readOnly
            </prop>
        </props>
    </property>
</bean>

 

请注意,和 清单 1 惟一的区别是,现在所有的隔离级别都被设置为 ISOLATION_DEFAULT 。如果要用 清单 2 中的事务配置执行一个应用程序,该代码会顺利运行。然而,您很可能想知道当执行 save()delete()find() 方法时,使用哪个隔离级别。这个问题的答案取决于 “其依赖项”。隔离级别依赖于用于与数据库通信的数据源。

图 1 中的序列图说明了在执行 save() 方法时,OrderService 实现对象和两个数据访问对象(DAO)的交互。(正如从您的经验中得出的那样,DAO 主要用于将业务逻辑从存储访问/持久性代码中分离出来。)


图 1. OrderService 实现的 save() 方法的序列图
Save 命令用例序列图

这里 看全图。

在执行 OrderService 实现的 save() 方法时使用的隔离级别由在 OrderDAOCustomerDAO 数据访问对象中引用的数据源所声明。例如,如果 OrderDAO 被配置为从定义为具有 “未提交读” 隔离级别的数据源中获取连接,而 CustomerDAO 被配置为使用定义为具有 “序列化” 隔离级别的数据源,然后在通过 OrderDAO 对象访问数据时, save() 方法会使用 “未提交读” 隔离级别,而在通过 CustomerDAO 访问数据时,使用 “序列化” 隔离级别。但如果再回过头来看 清单 1 ,就会发现这并不是预期的目的。相反,在一个用例执行中,单个的隔离级别将被用于所有的数据访问(如 save()delete()find() 方法),即使不同的用例执行相同的数据库操作,并且对数据访问对象执行相同的调用集。继续读下去,看看如何实现这一目标。

 




回页首


解决方案

步骤 1

该解决方案是一个由 7 个步骤组成的过程,在此过程中利用了名为 JdbcOperations 的 Spring 接口,该接口可以在 org.springframework.jdbc.core 包中找到。正如 Spring 文档中所描述的那样,该接口能被轻易地模拟或保存。第一步是要创建一个名为 JdbcOperationsImpl 的类,该类实现 JdbcOperations 接口。该类也实现 ApplicationContextAware 接口。

JdbcOperations 接口需要许多数据库访问操作的实现。当然,您不应该(也不应该想要)编写如此低层的代码。相反,此类的目的仅仅是作为一个代理,该代理将所有的数据访问调用转发至一个 org.springframework.jdbc.core.JdbcTemplate 实例。

您也许会回想起之前用 Spring 编写数据访问代码的经历,可以轻易地通过将一个 javax.sql.DataSource 实例传给 JdbcTemplate 的构造函数将其实例化。请记住,本文假设您正在使用一个应用服务器,该服务器将数据源定义作为隔离级别值的占位符。为在执行用例时使用相同的隔离级别,必须在执行该用例时,使用相同的 JdbcTemplate 实例来跨越所有的数据访问对象。换言之,依赖于执行中的用例,数据访问对象需要获得对 JdbcTemplate 实例的引用,该实例与(通过其 DataSource 对象)相应的隔离级别值相关联。

ApplicationContextAware 接口需要 setApplicationContext() 方法的一个实现,该方法将实现类的访问提供给 Spring 应用程序的上下文。正如稍后将会看到的那样,访问 Spring 的上下文是必需的,因为 JdbcOperationsImpl 使用它来获取 bean(通过其 ID)。JdbcOperationsImpl 类的 bean 定义如清单 3 所示:


清单 3. JdbcOperationsImpl 实例的定义

                
<bean id="jdbcOperations"
  class="application.storage.JdbcOperationsImpl" singleton="true">
    <constructor-arg index="0">
        <!-- Reference to a JdbcTemplate instance with a
          "read committed" isolation level -->
        <ref local="rcJdbcTemplate" />
    </constructor-arg>
</bean>

 

步骤 2

第二步是要确保所有的数据访问对象使用 JdbcOperationsImpl 类的一个实例来与数据库进行通信,而不是 JdbcTemplate 实例。这是很明显的,因为 JdbcTemplate 类实现 JdbcOperations 接口。不需要改变数据访问对象中一行代码;只需要改变 Spring 配置文件中每个数据访问对象的配置。例如,最初的 OrderDAO 数据访问对象的定义是这样的:

<bean id="orderDAO"
  class="sample.dao.OrderDAOImpl" singleton="true">
    <property name="jdbcOperations">
        <ref local="jdbcTemplate" />
    </property>
</bean>

 

请将 OrderDAO 数据访问对象的定义改成这样:

<bean id="orderDAO"
  class="sample.dao.OrderDAOImpl" singleton="true">
    <property name="jdbcOperations">
       <ref local="jdbcOperations" />
    </property>
</bean>

 

现在,JdbcOperationsImpl 类中的所有访问存储资源(如 batchUpdate()execute() 方法)的方法都调用一个名为 getJdbcTemplate() 的方法,如清单 4 所示:


清单 4. JdbcOperationsImpl 类中 getJdbcTemplate() 方法的实现

                
private JdbcTemplate getJdbcTemplate() {
    try {
        return (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
    } catch (ClassCastException e) {
        logger.warn(
          "Using default JdbcTemplate instance.", e);

        return defaultJdbcTemplate;
    }
}

 

在这段代码中,getJdbcTemplate() 方法查询 Spring 应用程序的上下文以获取相应的 JdbcTemplate 实例。请注意,使用了 jdbcTemplatebean id 来查询上下文。同样,请注意如果在 getJdbcTemplate() 获取 JdbcTemplate 对象时发生错误,将返回对默认 JdbcTemplate 对象的引用。defaultJdbcTemplate 对象是使用 “已提交读” 隔离级别的 JdbcOperationsImpl 类的 JdbcTemplate 实例变量。JdbcOperationsImpl 类使用这个实例变量作为后备解决方案,以防相应的 JdbcTemplate 实例不能从应用程序的上下文中获取。(当发生这种情况时,会在日记中记一个警告。)此类的构造函数期望将默认的 JdbcTemplate 实例作为一个参数,如清单 5 所示:


清单 5. JdbcOperationsImpl 类的构造函数

                
public JdbcOperationsImpl(JdbcTemplate defaultJdbcTemplate) {
    super();
    this.defaultJdbcTemplate = defaultJdbcTemplate;
}

 

从清单 6 中可见,只要要求应用程序的上下文返回标识为 jdbcTemplate 的对象,就会调用 IsolationLevelUtil 类的 getJdbcTemplate() 方法:


清单 6. jdbcTemplate bean 的定义

                
<bean id="jdbcTemplate"
  class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass">
        <value>application.services.IsolationLevelUtil</value>
    </property>
    <property name="targetMethod">
        <value>getJdbcTemplate</value>
    </property>
    <property name="singleton">
        <value>false</value>
    </property>
</bean>

 

步骤 3

第三步是用 清单 6 显示的定义更新 Spring 配置文件,并定义 IsolationLevelUtil 类的实现,如清单 7 所示:


清单 7. IsolationLevelUtil 类的实现

                
public class IsolationLevelUtil {

    private static final ThreadLocal threadJdbcTemplate = new ThreadLocal();

    private IsolationLevelUtil() {
        super();
    }

    public static JdbcTemplate getJdbcTemplate() {
        JdbcTemplate jdbcTemplate = (JdbcTemplate) threadJdbcTemplate.get();
        return jdbcTemplate;
    }

    public static void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        threadJdbcTemplate.set(jdbcTemplate);
    }
}

 

IsolationLevelUtil 类的 getJdbcTemplate() 方法返回和当前执行线程关联在一起的 JdbcTemplate 实例。名为 threadJdbcTemplate 的本地线程变量被用于保持线程和 JdbcTemplate 实例间的关联。您也许想知道为什么 JdbcOperationsImpl 类的 getJdbcTemplate() 方法没有显式地调用 IsolationLevelUtilgetJdbcTemplate() 方法。尽管这个方法会起作用,但更好的设计是让这两个类保持解耦。例如,如果想要实现一种不同的机制来获取和执行中的用例相应的 JdbcTemplate 实例,只需要改变 Spring 配置文件,而不是 JdbcOperationsImpl 类。

步骤 4

如果您正在思考哪个组件将相应的 JdbcTemplate 实例设置为 IsolationLevelUtil 类上的本地线程变量,您的思路是正确的。为此,这个值必须在线程执行的前期已经设置好了。否则,将返回 NULL 值。所以,第四步是编写一个负责设置名为 threadJdbcTemplate 的本地线程变量的组件。请将这个组件实现为一个名为 IsolationLevelAdvice 的面向方面的 advice,如清单 8 所示。这个 advice 在用例开始执行前即被应用。


清单 8. IsolationLevelAdvice 类的实现

                
public class IsolationLevelAdvice implements MethodInterceptor {

    private Map methodJdbcTemplateMap;

    private JdbcTemplate defaultJdbcTemplate;

    public IsolationLevelAdvice(Map methodJdbcTemplateMap,
      JdbcTemplate defaultJdbcTemplate) {

        super();
        this.defaultJdbcTemplate = defaultJdbcTemplate;
        this.methodJdbcTemplateMap = methodJdbcTemplateMap;
    }

    public Object invoke(MethodInvocation invocation) throws Exception {
        boolean set = false;
        try {
            Method method = invocation.getMethod();
            set = setThreadJdbcTemplate(method);
            Object rval = invocation.proceed();
            return rval;
        } finally {
            if (set) {
                unsetThreadJdbcTemplate();
            }
        }
    }

    public boolean setThreadJdbcTemplate(Method method) {

        boolean set = false;
        if (IsolationLevelUtil.getJdbcTemplate() == null) {
            JdbcTemplate jdbcTemplate = null;
            String methodName = method.getName();
            Iterator methodPatterns = methodJdbcTemplateMap.keySet().iterator();
            while (methodPatterns.hasNext()) {
                String methodPattern = (String) methodPatterns.next();
                if (Pattern.matches(methodPattern, methodName)) {
                    jdbcTemplate = (JdbcTemplate) 
                      methodJdbcTemplateMap.get(methodPattern);
                    break;
                }
            }
            if (jdbcTemplate == null) {
                jdbcTemplate = defaultJdbcTemplate;
            }

            IsolationLevelUtil.setJdbcTemplate(jdbcTemplate);
            set = true;
        }
        return set;
    }

    public void unsetThreadJdbcTemplate() {
        IsolationLevelUtil.setJdbcTemplate(null);
    }
}

 

在该应用程序中,每个服务对象实现都需要此类的实例。

步骤 5

第五步是要在 Spring 配置文件中定义这个类的一个 bean 定义,该 bean 将和 OrderService 实现类关联起来,如清单 9 所示:


清单 9.针对 OrderService 实现的隔离 advice bean 的定义

                
<bean id="orderServiceIsolationAdvice" 
  class="application.services.IsolationLevelAdvice" singleton="true">
    <constructor-arg index="0">
        <map>
            <entry key="save.*">
                <ref local="rrJdbcTemplate" />
             </entry>
             <entry key="delete.*">
                 <ref local="rcJdbcTemplate" />
             </entry>
             <entry key="find.*">
                 <ref local="rcJdbcTemplate" />
             </entry>
        </map>
    </constructor-arg>
    <constructor-arg index="1">
        <ref local="rcJdbcTemplate" />
     </constructor-arg>
</bean>

 

清单 9 中 bean 的定义显示了 IsolationLevelAdvice 类的实例的构造函数将一个对象映射表作为第一个参数。这个映射表使用字符串匹配模式作为定义在 OrderService 接口中方法的名称的键。这些模式中的每一个都被映射到一个 JdbcTemplate 实例中,该实例具有必须用于用例执行的隔离级别。构造函数的第二个参数指定 JdbcTemplate 实例,使用该实例是为了防止没有 JdbcTemplate 对象被映射到已经调用的方法中。如果在 清单 8 中仔细观察这个类的实现,会看到 IsolationLevelAdvice 实例将在运行时使用反射来确定要在 OrderService 实现对象上调用哪个方法。在确定了将执行的方法的名称后,该 advice 实例查询 methodJdbcTemplateMap 实例变量(methodJdbcTemplateMap 对象是对这个类的构造函数中第一个参数的引用)来确定在执行该用例时要使用哪个 JdbcTemplate

步骤 6

第六步是要指定 IsolationLevelAdvice bean(被标识为 orderServiceIsolationAdvice )和 OrderService 实现对象间的关联。清单 10 中显示的 bean 定义通过让 Spring 容器(被 IsolationLevelAdvice 实例标识为 orderServiceIsolationAdvice )充当 OrderService 类实现的 advice 正好完成这项任务:


清单 10. 针对 OrderService 实现的 AOP 代理 bean 的定义

                
<bean id="orderServiceTarget" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces">
        <value>application.services.OrderService</value>
    </property>
    <property name="interceptorNames">
        <value>orderServiceIsolationAdvice</value>
    </property>
    <property name="target">
        <ref bean="orderServiceImpl" />
    </property>
</bean>

 

步骤 7

第七步也是最后的一步是要定义应用程序所需的 JdbcTemplate 实例。清单 11 显示了每个实例的定义。每个 JdbcTemplate 定义都有一个对不同数据源对象的引用。由于有四个隔离级别,所以需要四个数据源定义和四个 JdbcTemplate 定义。清单 11 也显示了这些数据源定义:


清单 11. JdbcTemplate 和数据源对象的定义

                
<!-- "Serializable" isolation level - JdbcTemplate -->
<bean id="sJdbcTemplate"
  class="org.springframework.jdbc.core.JdbcTemplate" singleton="true">
    <property name="dataSource">
        <ref local="sDataSource" />
    </property>
</bean>

<!-- "Read repeatable" isolation level - JdbcTemplate -->
<bean id="rrJdbcTemplate"
  class="org.springframework.jdbc.core.JdbcTemplate" singleton="true">
    <property name="dataSource">
        <ref local="rrDataSource" />
    </property>
</bean>

<!-- "Read committed" isolation level - JdbcTemplate -->
<bean id="rcJdbcTemplate"
  class="org.springframework.jdbc.core.JdbcTemplate" singleton="true">
    <property name="dataSource">
        <ref local="rcDataSource" />
    </property>
</bean>

<!-- "Read uncommitted" isolation level - JdbcTemplate -->
<bean id="ruJdbcTemplate"
  class="org.springframework.jdbc.core.JdbcTemplate" singleton="true">
    <property name="dataSource">
        <ref local="ruDataSource" />
    </property>
</bean>

<!-- "Serializable" isolation level - data source -->
<bean id="sDataSource"
  class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/s_ds</value>
    </property>
</bean>

<!-- "Repeatable read" isolation level - data source -->
<bean id="rrDataSource"
  class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/rr_ds</value>
    </property>
</bean>

<!-- "Read committed" isolation level - data source -->
<bean id="rcDataSource"
  class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/rc_ds</value>
    </property>
</bean>

<!-- "Read uncommitted" isolation level - data source -->
<bean id="ruDataSource"
  class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/ru_ds</value>
    </property>
</bean>

 

图 2 中的类图撷取了这些类中存在的关系,定义这些类是为了实现我所描述过的解决方案:


图 2. 本文解决方案的类图
类图

这里 查看全图。

在这个类图中显示的大多数关系并没有定义在 Java 源代码中,而是在 Spring 配置文件中。(这对 Spring 用户来说并不奇怪。)同样,如果将我探讨过的 Spring bean 的定义和该类图中的实体作比较,很容易看出,在 图 2 中被标识为 orderServiceIsolationAdvicerrTemplatercTemplate 的类在本质上并不是 Java 类。这三个类中的每个类都有一个 Spring bean 的定义(而不是 Java 类文件)。为在类图中传达这个信息,我使用了在 IsolationLevelAdvice 类和 orderServiceIsolationAdvice 间以及在 JdbcTemplate 类和 rrTemplatercTemplate 间的 “绑定关系”。orderServiceIsolationAdvicerrTemplatercTemplate 实体只不过是通过将其模板类的参数和实际值绑定起来从而实例化其相应的 “模板类” 的具体对象。

下载 这些类的完整的源代码,您需要这些类来实现我在本文中演示的解决方案。

 




回页首


结束语

如 果在技术需求中声明了在执行使用分布式事务的用例过程中应该使用相同的隔离级别,尽管 JTA 事务管理器不支持自定义隔离级别,但您的应用程序能够满足此项需求。本文提供了实现此目标的一种方法,即使用 Spring 的依赖项-注入功能来保持类的解耦。第一眼看去,该实现似乎有点复杂,但您会意识到它很直白且相当简单。在执行用例时的任何时刻访问数据库,它让您在一种 可配置的方式下使用相同的隔离级别。处理业务逻辑和持久性逻辑的 Java 代码并未改变。相反,使用在 Spring 配置文件中的设置,所有 “神奇的事情” 都在运行时发生。这就是该解决方案的设计中的主要优点之一:使实现应用程序业务逻辑和持久性逻辑的类从确保在执行用例过程中使用的相同隔离级别的类和组件 中解耦出来。

分享到:
评论

相关推荐

    Spring攻略(第二版 中文高清版).part1

    1.7 使用Spring的FactoryBean创建Bean 27 1.7.1 问题 27 1.7.2 解决方案 27 1.7.3 工作原理 27 1.8 使用工厂Bean和Utility Schema定义集合 29 1.8.1 问题 29 1.8.2 解决方案 29 1.8.3 工作原理 29 ...

    Spring.3.x企业应用开发实战(完整版).part2

    9.1.4 事务隔离级别 9.1.5 JDBC对事务支持 9.2 ThreadLocal基础知识 9.2.1 ThreadLocal是什么 9.2.2 ThreadLocal的接口方法 9.2.3 一个TheadLocal实例 9.2.4 与Thread同步机制的比较 9.2.5 Spring使用ThreadLocal...

    Spring攻略(第二版 中文高清版).part2

    1.7 使用Spring的FactoryBean创建Bean 27 1.7.1 问题 27 1.7.2 解决方案 27 1.7.3 工作原理 27 1.8 使用工厂Bean和Utility Schema定义集合 29 1.8.1 问题 29 1.8.2 解决方案 29 1.8.3 工作原理 29 ...

    Spring面试题

    FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。 IOC 示例 理解控制反转最简单的方式就是看它的实际应用。在对由三部分组成的 Spring 系列 的第 1 部分进行总结时,我使用了一个示例,...

    Spring3.x企业应用开发实战(完整版) part1

    9.1.4 事务隔离级别 9.1.5 JDBC对事务支持 9.2 ThreadLocal基础知识 9.2.1 ThreadLocal是什么 9.2.2 ThreadLocal的接口方法 9.2.3 一个TheadLocal实例 9.2.4 与Thread同步机制的比较 9.2.5 Spring使用ThreadLocal...

    ssh(structs,spring,hibernate)框架中的上传下载

     需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中,...

    JavaWeb框架Wint.zip

    wint 是一个基于mvc易用的 java web框架,抛开了struts2 繁琐的配置,结合django,RoR,webx,play等框架的特点,使用约定优于配置的原则,使开发者能快速的搭建web和进行快速开发。 wint提供的主要功能有: 模板与...

    JCL:Jar Class Loader,一个可配置的动态自定义类加载器,旨在在IoC框架和Web应用程序中创建,管理和操作隔离的Java类加载器。

    这样做的动机是创建隔离的类加载器,该类加载器可以轻松地与IoC框架(如Spring)和Web应用程序集成。 整个库,包括其代码库和文档,都可以在的条款和条件下。 安装 要使用JCL ,请下载并构建JCL项目,并将jcl(-...

    liteflow:轻巧实用的微处理框架

    框架提供自定义配置源,只需实现一个接口,即可从任何地方加载配置源 支持SpringBoot的自动装配,也支持Spring的配置和非Spring的项目 提供串行和并行2种模式,提供常见常见的表达式语句 提供无级嵌套的显式子流程...

    Nepxion Discovery【探索】框架指南 V5.4.0.pdf

    Nepxion Discovery【探索】使用指南,基于Spring Cloud Greenwich版、Finchley版和Hoxton版而 制作,对于Edgware版,使用者需要自行修改。使用指南主要涉及的功能包括: 基于Header传递的全链路灰度路由,网关为路由...

    基于JavaWEB+SSM+mysql框架构建的在线商城系统源码+数据库+项目说明(课程设计).zip

    * 本项目为 Maven 项目,后端使用 Spring 4 + SpringMVC 4 + Mybatis 3.4 + aspectj 1.8 * 实现了一个 **通用mapper**,免写 SQL,可进行单表和多表关联查询,自动插入一对多/多对一对象(注解配置关联对象,结合 ...

    基于springBoot自研微服务框架+源代码+文档说明

    ## 自研微服务框架,借鉴springCloud全家桶的微服务设计思想 #### 支持可插拔的客户端服务端注解 #### 支持三种负载设计: 1.@OrionTarget注解定向负载 2.客户端自定义负载策略 balanceStrategy,继承统一的接口...

    基于springboot , zookeeper , redis 分布式事务强一致性方案+源代码+文档说明

    同时具备一定的扩展性与兼容性,因为存在自定义的服务框架,或者以后会涌现出更多的流行分布式服务框架,所以会提供一些组件适配自定义服务框架。 ## Maven依赖 ```java &lt;groupId&gt;com.github.cjyican&lt;/groupId&gt; ...

    SaaS微信小程序电商系统一键生成小程序源码

    采用Spring+SpringMVC+Mybatis主流开源框架,遵循MVC架构,设计轻巧,使用简单,开发人员接手与二次开发简单易懂;项目依赖的核心支持jar包,都已经完全开源. 项目完成了对阿里云、腾讯云、微信生态的快速接入与代码...

    java实现校园一卡通源码-resume:个人简历

    系统内部使用Spring cloud 生态圈技术进行,根据当前业务拆分商品 订单 会员等服务治理,使用eureka作为服务注册中心,使用spring config进行统一配置管理,spring cloud bus进行项目配置实时变化更新,spring boo

    MF00341-SaaS微信小程序电商生成小程序源码.zip

    采用Spring+SpringMVC+Mybatis主流开源框架,遵循MVC架构,设计轻巧,使用简单,开发人员接手与二次开发简单易懂; 项目依赖的核心支持jar包,都已经完全开源. 项目完成了对阿里云、腾讯云、微信生态的快速接入与...

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    lamp-cloud微服务脚手架

    使用Mybatis拦截器实现对所有SQL的拦截,修改默认的Schema,从而实现多租户数据隔离的目的。 并且支持可插拔。 9、二级缓存 采用J2Cache操作缓存,第一级缓存使用内存(Caffeine),第二级缓存使用 Redis。 由于大量的...

    asp.net知识库

    事务隔离性的一些基础知识 在组件之间实现事务和异步提交事务(NET2.0) 其它 在.NET访问MySql数据库时的几点经验! 自动代码生成器 关于能自定义格式的、支持多语言的、支持多数据库的代码生成器的想法 发布Oracle...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    3.4.3 使用自定义转换器 159 3.4.4 绑定到Bean属性的转换器 159 3.5 使用验证器进行输入校验 161 3.5.1 输入校验概述 161 3.5.2 JSF内置校验器 162 3.5.3 校验失败后的错误消息 163 3.5.4 必填校验器 165 3.6 自定义...

Global site tag (gtag.js) - Google Analytics