32. JCA CCI

32.1 Introduction

Java EE 提供了一个规范来标准化对企业信息系统(EIS)的访问:JCA(Java EE 连接器体系结构)。该规范分为几个不同的部分:

  • 连接器提供程序必须实现的 SPI(服务提供程序接口)。这些接口构成了资源适配器,可以将其部署在 Java EE 应用程序服务器上。在这种情况下,服务器将 Management 连接池,事务和安全性(托管模式)。应用服务器还负责 Management 配置,该配置保存在 Client 端应用程序外部。连接器也可以在没有应用程序服务器的情况下使用。在这种情况下,应用程序必须直接对其进行配置(非托管模式)。

  • 应用程序可以用来与连接器交互并由此与 EIS 通信的 CCI(通用 Client 端接口)。还提供了用于本地事务划分的 API。

Spring CCI 支持的目的是提供类,以利用 Spring Framework 的常规资源和事务 Management 工具来访问典型的 Spring 风格的 CCI 连接器。

Note

连接器的 Client 端始终不使用 CCI。一些连接器公开自己的 API,仅提供 JCA 资源适配器以使用 Java EE 容器的系统协定(连接池,全局事务,安全性)。 Spring 没有为此类特定于连接器的 API 提供特殊支持。

32.2 配置 CCI

32.2.1 连接器配置

使用 JCA CCI 的基本资源是ConnectionFactory接口。使用的连接器必须提供此接口的实现。

要使用连接器,可以将其部署在应用程序服务器上,并从服务器的 JNDI 环境(托管模式)中获取ConnectionFactory。连接器必须打包为 RAR 文件(资源适配器 Files),并包含ra.xml文件来描述其部署特性。资源的实际名称是在部署时指定的。要在 Spring 中访问它,只需使用 Spring 的JndiObjectFactoryBean/<jee:jndi-lookup>通过其 JNDI 名称获取工厂即可。

使用连接器的另一种方法是将其嵌入到您的应用程序中(非托管模式),而不使用应用程序服务器来部署和配置它。 Spring 提供了通过提供的FactoryBean(LocalConnectionFactoryBean)将连接器配置为 bean 的可能性。通过这种方式,您只需要在 Classpath 中使用连接器库(不需要 RAR 文件和ra.xmlDescriptors)。如有必要,必须从连接器的 RAR 文件中提取该库。

获得对ConnectionFactory实例的访问权限后,可以将其注入到组件中。这些组件可以根据普通的 CCI API 进行编码,也可以利用 Spring 的支持类进行 CCI 访问(例如CciTemplate)。

Note

在非托管模式下使用连接器时,不能使用全局事务,因为资源永远不会在当前线程的当前全局事务中被征用/除名。该资源根本不知道可能正在运行的任何全局 Java EE 事务。

Spring 中的 ConnectionFactory 配置

为了构建与 EIS 的连接,如果您处于托管模式,则需要从应用程序服务器获取ConnectionFactory;如果处于非托管模式,则需要直接从 Spring 获取。

在托管模式下,您可以从 JNDI 访问ConnectionFactory;其属性将在应用程序服务器中配置。

<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/>

在非托管模式下,必须将要在 Spring 的配置中使用的ConnectionFactory配置为 JavaBean。 LocalConnectionFactoryBean类提供了这种设置样式,传入了连接器的ManagedConnectionFactory实现,公开了应用程序级 CCI ConnectionFactory

<bean id="eciManagedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
    <property name="serverName" value="TXSERIES"/>
    <property name="connectionURL" value="tcp://localhost/"/>
    <property name="portNumber" value="2006"/>
</bean>

<bean id="eciConnectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/>
</bean>

Note

您不能直接实例化特定的ConnectionFactory。您需要为连接器完成ManagedConnectionFactory接口的相应实现。该接口是 JCA SPI 规范的一部分。

32.2.3 配置 CCI 连接

JCA CCI 允许开发人员使用连接器的ConnectionSpec实现来配置到 EIS 的连接。为了配置其属性,您需要使用专用适配器ConnectionSpecConnectionFactoryAdapter包装目标连接工厂。因此,专用ConnectionSpec可以配置为connectionSpec属性(作为内部 bean)。

此属性不是必需的,因为 CCI ConnectionFactory接口定义了两种不同的方法来获取 CCI 连接。某些ConnectionSpec属性通常可以在应用程序服务器中(以托管模式)或在相应的本地ManagedConnectionFactory实现中进行配置。

public interface ConnectionFactory implements Serializable, Referenceable {
    ...
    Connection getConnection() throws ResourceException;
    Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException;
    ...
}

Spring 提供了一个ConnectionSpecConnectionFactoryAdapter,它允许指定一个ConnectionSpec实例用于给定工厂的所有操作。如果指定了适配器的connectionSpec属性,则适配器使用带有ConnectionSpec参数的getConnection变体,否则使用不带参数的变体。

<bean id="managedConnectionFactory"
        class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory">
    <property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/>
    <property name="driverName" value="org.hsqldb.jdbcDriver"/>
</bean>

<bean id="targetConnectionFactory"
        class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="connectionFactory"
        class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
    <property name="connectionSpec">
        <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
            <property name="user" value="sa"/>
            <property name="password" value=""/>
        </bean>
    </property>
</bean>

32.2.4 使用单个 CCI 连接

如果要使用单个 CCI 连接,Spring 会提供另一个ConnectionFactory适配器来 Management 此连接。 SingleConnectionFactory适配器类将延迟打开单个连接,并在应用程序关闭时销毁该 bean 时将其关闭。此类将暴露具有相应行为的特殊Connection代理,它们均共享相同的基础物理连接。

<bean id="eciManagedConnectionFactory"
        class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
    <property name="serverName" value="TEST"/>
    <property name="connectionURL" value="tcp://localhost/"/>
    <property name="portNumber" value="2006"/>
</bean>

<bean id="targetEciConnectionFactory"
        class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="eciManagedConnectionFactory"/>
</bean>

<bean id="eciConnectionFactory"
        class="org.springframework.jca.cci.connection.SingleConnectionFactory">
    <property name="targetConnectionFactory" ref="targetEciConnectionFactory"/>
</bean>

Note

无法使用ConnectionSpec直接配置此ConnectionFactory适配器。如果您需要特定ConnectionSpec的单个连接,请使用SingleConnectionFactory与之对话的中介ConnectionSpecConnectionFactoryAdapter

32.3 使用 Spring 的 CCI 访问支持

32.3.1 记录转换

JCA CCI 支持的目的之一是为处理 CCI 记录提供便利的设施。开发人员可以指定策略来创建记录并从 Logging 提取数据,以用于 Spring 的CciTemplate。如果您不想直接在应用程序中使用记录,则以下界面将配置策略以使用 Importing 和输出记录。

为了创建 ImportingRecord,开发人员可以使用RecordCreator接口的专用实现。

public interface RecordCreator {

    Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException;

}

如您所见,createRecord(..)方法接收一个RecordFactory实例作为参数,该实例与所使用的ConnectionFactoryRecordFactory相对应。该引用可用于创建IndexedRecordMappedRecord实例。下面的示例演示如何使用RecordCreator接口和索引/Map 的记录。

public class MyRecordCreator implements RecordCreator {

    public Record createRecord(RecordFactory recordFactory) throws ResourceException {
        IndexedRecord input = recordFactory.createIndexedRecord("input");
        input.add(new Integer(id));
        return input;
    }

}

输出Record可用于从 EIS 接收数据。因此,可以将RecordExtractor接口的特定实现传递给 Spring 的CciTemplate,以从输出Record提取数据。

public interface RecordExtractor {

    Object extractData(Record record) throws ResourceException, SQLException, DataAccessException;

}

以下示例显示了如何使用RecordExtractor界面。

public class MyRecordExtractor implements RecordExtractor {

    public Object extractData(Record record) throws ResourceException {
        CommAreaRecord commAreaRecord = (CommAreaRecord) record;
        String str = new String(commAreaRecord.toByteArray());
        String field1 = string.substring(0,6);
        String field2 = string.substring(6,1);
        return new OutputObject(Long.parseLong(field1), field2);
    }

}

32.3.2 CciTemplate

CciTemplate是核心 CCI 支持包(org.springframework.jca.cci.core)的中心类。由于它处理资源的创建和释放,因此它简化了 CCI 的使用。这有助于避免常见的错误,例如忘记始终关闭连接。它关心连接和交互对象的生命周期,让应用程序代码专注于从应用程序数据生成 Importing 记录并从输出 Logging 提取应用程序数据。

JCA CCI 规范定义了两种不同的方法来调用 EIS 上的操作。 CCI Interaction接口提供两个执行方法签名:

public interface javax.resource.cci.Interaction {

    ...

    boolean execute(InteractionSpec spec, Record input, Record output) throws ResourceException;

    Record execute(InteractionSpec spec, Record input) throws ResourceException;

    ...

}

根据调用的模板方法,CciTemplate将知道在交互中要调用哪个execute方法。无论如何,必须正确初始化InteractionSpec实例。

CciTemplate.execute(..)可以通过两种方式使用:

  • 具有直接的Record参数。在这种情况下,您只需要传递 CCIImporting 记录,而返回的对象就是相应的 CCI 输出记录。

  • 对于应用程序对象,使用记录 Map。在这种情况下,您需要提供相应的RecordCreatorRecordExtractor实例。

对于第一种方法,将使用以下模板方法。这些方法直接对应于Interaction界面上的方法。

public class CciTemplate implements CciOperations {

    public Record execute(InteractionSpec spec, Record inputRecord)
            throws DataAccessException { ... }

    public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord)
            throws DataAccessException { ... }

}

使用第二种方法,我们需要指定记录创建和记录提取策略作为参数。使用的接口是上一部分有关记录转换的接口。相应的CciTemplate方法如下:

public class CciTemplate implements CciOperations {

    public Record execute(InteractionSpec spec,
            RecordCreator inputCreator) throws DataAccessException {
        // ...
    }

    public Object execute(InteractionSpec spec, Record inputRecord,
            RecordExtractor outputExtractor) throws DataAccessException {
        // ...
    }

    public Object execute(InteractionSpec spec, RecordCreator creator,
            RecordExtractor extractor) throws DataAccessException {
        // ...
    }

}

除非在模板上设置了outputRecordCreator属性(请参见下一节),否则每个方法都将使用两个参数调用 CCI Interaction的相应execute方法:InteractionSpec和 ImportingRecord,并接收输出Record作为返回值。

CciTemplate还提供了通过createIndexRecord(..)createMappedRecord(..)方法在RecordCreator实现之外创建IndexRecordMappedRecord的方法。可以在 DAO 实现中使用它来创建Record实例以传递到相应的CciTemplate.execute(..)方法中。

public class CciTemplate implements CciOperations {

    public IndexedRecord createIndexedRecord(String name) throws DataAccessException { ... }

    public MappedRecord createMappedRecord(String name) throws DataAccessException { ... }

}

32.3.3 DAO 支持

Spring 的 CCI 支持为 DAO 提供了一个抽象类,支持注入ConnectionFactoryCciTemplate实例。该类的名称是CciDaoSupport:它提供了简单的setConnectionFactorysetCciTemplate方法。在内部,此类将为传入的ConnectionFactory创建一个CciTemplate实例,并将其暴露给子类中的具体数据访问实现。

public abstract class CciDaoSupport {

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        // ...
    }

    public ConnectionFactory getConnectionFactory() {
        // ...
    }

    public void setCciTemplate(CciTemplate cciTemplate) {
        // ...
    }

    public CciTemplate getCciTemplate() {
        // ...
    }

}

32.3.4 自动输出记录生成

如果使用的连接器仅支持以 Importing 和输出记录作为参数的Interaction.execute(..)方法(也就是说,它需要传递所需的输出记录,而不是返回适当的输出记录),则可以将CciTemplateoutputRecordCreator属性设置为自动收到响应后,将生成一个输出记录,以供 JCA 连接器填充。然后,该记录将返回给模板的调用者。

该属性仅包含用于该目的的RecordCreator接口的实现。 RecordCreator接口已经在第 32.3.1 节“记录转换”中讨论过。 outputRecordCreator属性必须直接在CciTemplate上指定。可以在应用程序代码中完成,如下所示:

cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator());

或(建议)在 Spring 配置中(如果CciTemplate被配置为专用 bean 实例):

<bean id="eciOutputRecordCreator" class="eci.EciOutputRecordCreator"/>

<bean id="cciTemplate" class="org.springframework.jca.cci.core.CciTemplate">
    <property name="connectionFactory" ref="eciConnectionFactory"/>
    <property name="outputRecordCreator" ref="eciOutputRecordCreator"/>
</bean>

Note

由于CciTemplate类是线程安全的,因此通常将其配置为共享实例。

32.3.5 Summary

下表总结了CciTemplate类的机制以及在 CCI Interaction接口上调用的相应方法:

表 32.1. 交互执行方法的用法

CciTemplate 方法签名CciTemplate outputRecordCreator 属性在 CCI 交互上调用的 execute 方法
记录执行(InteractionSpec,记录)not set记录执行(InteractionSpec,记录)
记录执行(InteractionSpec,记录)setboolean execute(InteractionSpec,Record,Record)
无效执行(InteractionSpec,记录,记录)not set无效执行(InteractionSpec,记录,记录)
无效执行(InteractionSpec,记录,记录)set无效执行(InteractionSpec,记录,记录)
记录执行(InteractionSpec,RecordCreator)not set记录执行(InteractionSpec,记录)
记录执行(InteractionSpec,RecordCreator)set无效执行(InteractionSpec,记录,记录)
记录执行(InteractionSpec,Record,RecordExtractor)not set记录执行(InteractionSpec,记录)
记录执行(InteractionSpec,Record,RecordExtractor)set无效执行(InteractionSpec,记录,记录)
记录执行(InteractionSpec,RecordCreator,RecordExtractor)not set记录执行(InteractionSpec,记录)
记录执行(InteractionSpec,RecordCreator,RecordExtractor)set无效执行(InteractionSpec,记录,记录)

32.3.6 直接使用 CCI 连接和交互

CciTemplate还提供了以与JdbcTemplateJmsTemplate相同的方式直接处理 CCI 连接和交互的可能性。例如,当您要对 CCI 连接或交互执行多个操作时,此功能很有用。

接口ConnectionCallback提供了一个 CCI Connection作为参数,以便对其执行自定义操作,此外还提供了创建Connection的 CCI ConnectionFactory。例如,后者对于获取关联的RecordFactory实例并创建索引/Map 记录很有用。

public interface ConnectionCallback {

    Object doInConnection(Connection connection, ConnectionFactory connectionFactory)
            throws ResourceException, SQLException, DataAccessException;

}

接口InteractionCallback提供 CCI Interaction,以便对其执行自定义操作,并提供相应的 CCI ConnectionFactory

public interface InteractionCallback {

    Object doInInteraction(Interaction interaction, ConnectionFactory connectionFactory)
        throws ResourceException, SQLException, DataAccessException;

}

Note

InteractionSpec对象可以在多个模板调用之间共享,也可以在每个回调方法中重新创建。这完全取决于 DAO 的实现。

32.3.7 CciTemplate 用法示例

在本部分中,CciTemplate的用法将显示为使用带有 IBM CICS ECI 连接器的 ECI 模式的 CICS。

首先,必须对 CCI InteractionSpec进行一些初始化,以指定要访问的 CICS 程序以及如何与之交互。

ECIInteractionSpec interactionSpec = new ECIInteractionSpec();
interactionSpec.setFunctionName("MYPROG");
interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);

然后程序可以通过 Spring 的模板使用 CCI 并指定自定义对象和 CCI Records之间的 Map。

public class MyDaoImpl extends CciDaoSupport implements MyDao {

    public OutputObject getData(InputObject input) {
        ECIInteractionSpec interactionSpec = ...;

    OutputObject output = (ObjectOutput) getCciTemplate().execute(interactionSpec,
        new RecordCreator() {
            public Record createRecord(RecordFactory recordFactory) throws ResourceException {
                return new CommAreaRecord(input.toString().getBytes());
            }
        },
        new RecordExtractor() {
            public Object extractData(Record record) throws ResourceException {
                CommAreaRecord commAreaRecord = (CommAreaRecord)record;
                String str = new String(commAreaRecord.toByteArray());
                String field1 = string.substring(0,6);
                String field2 = string.substring(6,1);
                return new OutputObject(Long.parseLong(field1), field2);
            }
        });

        return output;
    }
}

如前所述,回调可用于直接在 CCI 连接或交互上工作。

public class MyDaoImpl extends CciDaoSupport implements MyDao {

    public OutputObject getData(InputObject input) {
        ObjectOutput output = (ObjectOutput) getCciTemplate().execute(
            new ConnectionCallback() {
                public Object doInConnection(Connection connection,
                        ConnectionFactory factory) throws ResourceException {

                    // do something...

                }
            });
        }
        return output;
    }

}

Note

对于ConnectionCallback,将使用CciTemplate来 Management 和关闭所使用的Connection,但是在连接上创建的任何交互都必须由回调实现来 Management。

对于更具体的回调,您可以实现InteractionCallback。在这种情况下,传入的Interaction将由CciTemplateManagement 和关闭。

public class MyDaoImpl extends CciDaoSupport implements MyDao {

    public String getData(String input) {
        ECIInteractionSpec interactionSpec = ...;
        String output = (String) getCciTemplate().execute(interactionSpec,
            new InteractionCallback() {
                public Object doInInteraction(Interaction interaction,
                        ConnectionFactory factory) throws ResourceException {
                    Record input = new CommAreaRecord(inputString.getBytes());
                    Record output = new CommAreaRecord();
                    interaction.execute(holder.getInteractionSpec(), input, output);
                    return new String(output.toByteArray());
                }
            });
        return output;
    }

}

对于上面的示例,在非托管模式下,所涉及的 Spring Bean 的相应配置如下所示:

<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
    <property name="serverName" value="TXSERIES"/>
    <property name="connectionURL" value="local:"/>
    <property name="userName" value="CICSUSER"/>
    <property name="password" value="CICS"/>
</bean>

<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="component" class="mypackage.MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

在托管模式下(即,在 Java EE 环境中),配置可能如下所示:

<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/>

<bean id="component" class="MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

32.4 将 CCI 访问建模为操作对象

org.springframework.jca.cci.object软件包包含支持类,这些支持类允许您以不同的方式访问 EIS:通过可重用的操作对象,类似于 Spring 的 JDBC 操作对象(请参阅 JDBC 章)。这通常将封装 CCI API:将应用程序级 Importing 对象传递给操作对象,以便它可以构造 Importing 记录,然后将接收到的记录数据转换为应用程序级输出对象并返回它。

Note

这种方法在内部基于CciTemplate类和RecordCreator/RecordExtractor接口,重用了 Spring 核心 CCI 支持的机制。

32.4.1 MappingRecordOperation

MappingRecordOperation本质上与CciTemplate执行相同的工作,但是将特定的预配置操作表示为对象。它提供了两种模板方法来指定如何将 Importing 对象转换为 Importing 记录,以及如何将输出记录转换为输出对象(记录 Map):

  • createInputRecord(..)指定如何将 Importing 对象转换为 ImportingRecord

  • extractOutputData(..)指定如何从输出Record中提取输出对象

这些是这些方法的签名:

public abstract class MappingRecordOperation extends EisOperation {

    ...

    protected abstract Record createInputRecord(RecordFactory recordFactory,
            Object inputObject) throws ResourceException, DataAccessException {
        // ...
    }

    protected abstract Object extractOutputData(Record outputRecord)
            throws ResourceException, SQLException, DataAccessException {
        // ...
    }

    ...

}

此后,为了执行 EIS 操作,您需要使用单个 execute 方法,传入应用程序级 Importing 对象并接收应用程序级输出对象作为结果:

public abstract class MappingRecordOperation extends EisOperation {

    ...

    public Object execute(Object inputObject) throws DataAccessException {
    }

    ...
}

如您所见,与CciTemplate类相反,此execute(..)方法没有InteractionSpec作为参数。相反,InteractionSpec对操作是全局的。必须使用以下构造函数实例化具有特定InteractionSpec的操作对象:

InteractionSpec spec = ...;
MyMappingRecordOperation eisOperation = new MyMappingRecordOperation(getConnectionFactory(), spec);
...

32.4.2 MappingCommAreaOperation

一些连接器使用基于 COMMAREA 的记录,该记录表示一个字节数组,其中包含要发送到 EIS 的参数和它返回的数据。 Spring 提供了一个特殊的操作类,可以直接在 COMMAREA 上工作而不是在记录上工作。 MappingCommAreaOperation类扩展了MappingRecordOperation类以提供这种特殊的 COMMAREA 支持。它隐式地使用CommAreaRecord类作为 Importing 和输出记录类型,并提供了两种新方法将 Importing 对象转换为 ImportingCOMMAREA,并将输出 COMMAREA 转换为输出对象。

public abstract class MappingCommAreaOperation extends MappingRecordOperation {

    ...

    protected abstract byte[] objectToBytes(Object inObject)
            throws IOException, DataAccessException;

    protected abstract Object bytesToObject(byte[] bytes)
        throws IOException, DataAccessException;

    ...

}

32.4.3 自动输出记录生成

由于每个MappingRecordOperation子类内部都基于 CciTemplate,因此可以使用与CciTemplate相同的自动生成输出记录的方式。每个操作对象都提供相应的setOutputRecordCreator(..)方法。有关更多信息,请参见第 32.3.4 节“自动输出记录生成”

32.4.4 Summary

操作对象方法以与CciTemplate类相同的方式使用记录。

表 32.2. 交互执行方法的用法

MappingRecordOperation 方法签名MappingRecordOperation outputRecordCreator 属性在 CCI 交互上调用的 execute 方法
Object execute(Object)not set记录执行(InteractionSpec,记录)
Object execute(Object)setboolean execute(InteractionSpec,Record,Record)

32.4.5 MappingRecordOperation 用法示例

在本节中,将显示MappingRecordOperation的用法以使用 Blackbox CCI 连接器访问数据库。

Note

该连接器的原始版本由 Java EE SDK(1.3 版)提供,可从 Oracle 获得。

首先,必须对 CCI InteractionSpec进行一些初始化以指定要执行的 SQL 请求。在此示例中,我们直接定义了将请求的参数转换为 CCI 记录的方法以及将 CCI 结果记录转换为Person类的实例的方法。

public class PersonMappingOperation extends MappingRecordOperation {

    public PersonMappingOperation(ConnectionFactory connectionFactory) {
        setConnectionFactory(connectionFactory);
        CciInteractionSpec interactionSpec = new CciConnectionSpec();
        interactionSpec.setSql("select * from person where person_id=?");
        setInteractionSpec(interactionSpec);
    }

    protected Record createInputRecord(RecordFactory recordFactory,
            Object inputObject) throws ResourceException {
        Integer id = (Integer) inputObject;
        IndexedRecord input = recordFactory.createIndexedRecord("input");
        input.add(new Integer(id));
        return input;
    }

    protected Object extractOutputData(Record outputRecord)
            throws ResourceException, SQLException {
        ResultSet rs = (ResultSet) outputRecord;
        Person person = null;
        if (rs.next()) {
            Person person = new Person();
            person.setId(rs.getInt("person_id"));
            person.setLastName(rs.getString("person_last_name"));
            person.setFirstName(rs.getString("person_first_name"));
        }
        return person;
    }
}

然后,应用程序可以使用人员标识符作为参数来执行操作对象。请注意,操作对象可以设置为共享实例,因为它是线程安全的。

public class MyDaoImpl extends CciDaoSupport implements MyDao {

    public Person getPerson(int id) {
        PersonMappingOperation query = new PersonMappingOperation(getConnectionFactory());
        Person person = (Person) query.execute(new Integer(id));
        return person;
    }
}

在非托管模式下,Spring Bean 的相应配置如下所示:

<bean id="managedConnectionFactory"
        class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory">
    <property name="connectionURL" value="jdbc:hsqldb:hsql://localhost:9001"/>
    <property name="driverName" value="org.hsqldb.jdbcDriver"/>
</bean>

<bean id="targetConnectionFactory"
        class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="connectionFactory"
        class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
    <property name="connectionSpec">
        <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
            <property name="user" value="sa"/>
            <property name="password" value=""/>
        </bean>
    </property>
</bean>

<bean id="component" class="MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

在托管模式下(即,在 Java EE 环境中),配置可能如下所示:

<jee:jndi-lookup id="targetConnectionFactory" jndi-name="eis/blackbox"/>

<bean id="connectionFactory"
        class="org.springframework.jca.cci.connection.ConnectionSpecConnectionFactoryAdapter">
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
    <property name="connectionSpec">
        <bean class="com.sun.connector.cciblackbox.CciConnectionSpec">
            <property name="user" value="sa"/>
            <property name="password" value=""/>
        </bean>
    </property>
</bean>

<bean id="component" class="MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

32.4.6 MappingCommAreaOperation 用法示例

在本部分中,将显示MappingCommAreaOperation的用法:使用 IBM CICS ECI 连接器以 ECI 模式访问 CICS。

首先,需要初始化 CCI InteractionSpec以指定要访问哪个 CICS 程序以及如何与之交互。

public abstract class EciMappingOperation extends MappingCommAreaOperation {

    public EciMappingOperation(ConnectionFactory connectionFactory, String programName) {
        setConnectionFactory(connectionFactory);
        ECIInteractionSpec interactionSpec = new ECIInteractionSpec(),
        interactionSpec.setFunctionName(programName);
        interactionSpec.setInteractionVerb(ECIInteractionSpec.SYNC_SEND_RECEIVE);
        interactionSpec.setCommareaLength(30);
        setInteractionSpec(interactionSpec);
        setOutputRecordCreator(new EciOutputRecordCreator());
    }

    private static class EciOutputRecordCreator implements RecordCreator {
        public Record createRecord(RecordFactory recordFactory) throws ResourceException {
            return new CommAreaRecord();
        }
    }

}

然后可以将抽象EciMappingOperation类作为子类,以指定自定义对象和Records之间的 Map。

public class MyDaoImpl extends CciDaoSupport implements MyDao {

    public OutputObject getData(Integer id) {
        EciMappingOperation query = new EciMappingOperation(getConnectionFactory(), "MYPROG") {

            protected abstract byte[] objectToBytes(Object inObject) throws IOException {
                Integer id = (Integer) inObject;
                return String.valueOf(id);
            }

            protected abstract Object bytesToObject(byte[] bytes) throws IOException;
                String str = new String(bytes);
                String field1 = str.substring(0,6);
                String field2 = str.substring(6,1);
                String field3 = str.substring(7,1);
                return new OutputObject(field1, field2, field3);
            }
        });

        return (OutputObject) query.execute(new Integer(id));
    }

}

在非托管模式下,Spring Bean 的相应配置如下所示:

<bean id="managedConnectionFactory" class="com.ibm.connector2.cics.ECIManagedConnectionFactory">
    <property name="serverName" value="TXSERIES"/>
    <property name="connectionURL" value="local:"/>
    <property name="userName" value="CICSUSER"/>
    <property name="password" value="CICS"/>
</bean>

<bean id="connectionFactory" class="org.springframework.jca.support.LocalConnectionFactoryBean">
    <property name="managedConnectionFactory" ref="managedConnectionFactory"/>
</bean>

<bean id="component" class="MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

在托管模式下(即,在 Java EE 环境中),配置可能如下所示:

<jee:jndi-lookup id="connectionFactory" jndi-name="eis/cicseci"/>

<bean id="component" class="MyDaoImpl">
    <property name="connectionFactory" ref="connectionFactory"/>
</bean>

32.5 Transactions

JCA 为资源适配器指定了多个级别的事务支持。资源适配器支持的事务类型在其ra.xml文件中指定。基本上有三个选项:无(例如,使用 CICS EPI 连接器),本地事务(例如,使用 CICS ECI 连接器),全局事务(例如,使用 IMS 连接器)。

<connector>
    <resourceadapter>
        <!-- <transaction-support>NoTransaction</transaction-support> -->
        <!-- <transaction-support>LocalTransaction</transaction-support> -->
        <transaction-support>XATransaction</transaction-support>
    <resourceadapter>
<connector>

对于全局事务,可以使用 Spring 的通用事务基础结构来划分事务,以JtaTransactionManager作为后端(委托给下面的 Java EE 服务器的分布式事务处理协调器)。

对于单个 CCI ConnectionFactory上的本地事务,Spring 为 CCI 提供了一种特定的事务 Management 策略,类似于 JDBC 的DataSourceTransactionManager。 CCI API 定义了本地事务对象和相应的本地事务划分方法。 Spring 的CciLocalTransactionManager执行此类本地 CCI 事务,完全符合 Spring 的通用PlatformTransactionManager抽象。

<jee:jndi-lookup id="eciConnectionFactory" jndi-name="eis/cicseci"/>

<bean id="eciTransactionManager"
        class="org.springframework.jca.cci.connection.CciLocalTransactionManager">
    <property name="connectionFactory" ref="eciConnectionFactory"/>
</bean>

两种 Transaction 策略都可以与 Spring 的任何 Transaction 标界工具一起使用,无论是声明式还是程序式。这是 Spring 通用的PlatformTransactionManager抽象的结果,该抽象将事务划分与实际执行策略分离。只需根据需要在JtaTransactionManagerCciLocalTransactionManager之间切换,即可保持事务划分不变。

有关 SpringTransaction 功能的更多信息,请参见标题为第十七章,TransactionManagement的章节。