21.3. 使用Spring的 CCI访问支持

21.3.1. 记录转换

对JCA CCI支持的一个目标是提供方便的机制来操作CCI记录。 开发人员可以通过使用Spring的 CciTemplate 来指定创建记录并从记录中提取数据的策略。 如果你不想在应用程序中直接操作记录,可以使用下面的接口来配置用于输入输出记录的策略。

为了创建一个输入 Record,开发人员可以使用 RecordCreator 接口的一个特定实现。

public interface RecordCreator {

 Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException;
}

正如你所看到的一样, createRecord(..) 方法接收一个 RecordFactory 实例作为参数, 该参数对应于所使用的 ConnectionFactoryRecordFactory 接口。 它能被用于创建 IndexedRecord 或者 MappedRecord 的实例。 下面的例子展示了如何使用 RecordFactory 接口和索引(indexed)/映射(mapped)记录。

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);
 }
}

21.3.2. CciTemplate

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

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

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 类可以知道 interaction上的哪个 execute 方法被调用。 在任何情况下,都必须有一个正确初始化过的 InteractionSpec 实例。

CciTemplate.execute(..)可以以下列两种方式使用:

  • 在提供直接的 Record 参数的情况下,只需要传递输入记录给 CCI , 而返回的对象就是对应的 CCI 输出记录。

  • 在提供使用记录映射的应用对象的情况下,你需要提供相应的 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 中相应的含有两个参数: InteractionSpec 和输入 Recordexecute 方法, 并接收一个输出 Record 作为返回值。

通过 createIndexRecord(..)createMappedRecord(..) 方法, CciTemplateRecordCreator 实现类外部也提供了创建 IndexRecordMappedRecord。 还可以用来在DAO实现内创建记录实例并传入到相应的 CciTemplate.execute(..) 方法。

public class CciTemplate implements CciOperations {

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

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

}

21.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() { ... }

}

21.3.4. 自动输出记录生成

如果所用的连接器只支持以输入输出记录作为参数的 Interaction.execute(..) 方法 (就是说,它要求传入期望的输出记录而不是返回适当的输出记录), 你可以设定 CciTemplate 类的 outputRecordCreator 属性来自动生成一个输出记录, 当接收到响应时JCA连接器(JCA connector)将填充该记录并返回给模板的调用者。

因为这个目的,这个属性只持有 RecordCreator 接口的一个实现。 RecordCreator 接口已经在 第 21.3.1 节 “记录转换” 进行了讨论。 outputRecordCreator 属性必须直接在 CciTemplate 中指定,可以在应用代码中做到这一点。

cciTemplate.setOutputRecordCreator(new EciOutputRecordCreator());

或者如果 CciTemplate 被配置为一个专门的bean实例,那么outputRecordCreator还可以在Spring文件中配置(推荐的做法):

<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>

注意

因为 CciTemplate 类是线程安全的,所以它通常被配置为一个共享实例。

21.3.5. 总结

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

表 21.1. Usage of Interaction execute methods

CciTemplate method signature CciTemplate outputRecordCreator property execute method called on the CCI Interaction
Record execute(InteractionSpec, Record) not set Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, Record) set boolean execute(InteractionSpec, Record, Record)
void execute(InteractionSpec, Record, Record) not set void execute(InteractionSpec, Record, Record)
void execute(InteractionSpec, Record, Record) set void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, RecordCreator) not set Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, RecordCreator) set void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, Record, RecordExtractor) not set Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, Record, RecordExtractor) set void execute(InteractionSpec, Record, Record)
Record execute(InteractionSpec, RecordCreator, RecordExtractor) not set Record execute(InteractionSpec, Record)
Record execute(InteractionSpec, RecordCreator, RecordExtractor) set void execute(InteractionSpec, Record, Record)


21.3.6. 直接使用一个CCI Connection接口和Interaction接口

类似于 JdbcTemplate 类和 JmsTemplate 类的操作方式, CciTemplate 类同样提供直接操作CCI 连接和交互的可能性。 比如说如果你想对一个CCI连接执行多种操作,这就会很有用。

ConnectionCallback 接口提供以CCI Connection 作为参数, 为了在它上面执行自定义动作,添加了创建 Connection 的CCI ConnectionFactory。 后者在获取相关 RecordFactory 实例和创建indexed/mapped records时很有用。例如:

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;
}

注意

InteractionSpec 对象还可以在多个template调用之间被共享或者在每个回调方法内重新创建, 这完全取决于 DAO 的实现。

21.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 之间指定映射。

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;
 }
}

正如之前讨论的那样,callbacks 可以被用来直接在 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;
 }
}

注意

当getCciTemplate().execute参数是 ConnectionCallback 时, 所用的 Connection 将被 CciTemplate 管理和关闭, 但是任何在连接上建立的交互都必须被callback实现类所管理。

对于一个更特殊的callback,你可以实现一个 InteractionCallback 。 这样传入的 Interaction 将会被 CciTemplate 管理和关闭。

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;
 }
}

在上面的例子中,非托管模式(non-managed)下对应的spring beans的配置会像下面这样:

<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>

在托管模式(managed mode)(也就是说,在一个J2EE环境)下,配置可能如下所示:

<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
 <property name="jndiName" value="eis/cicseci"/>
</bean>

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