21.4. 建模CCI访问为操作对象

org.springframework.jca.cci.object 包中包含的支持类允许你以另一种风格访问EIS: 通过可重用的操作对象,类似于Spring的JDBC操作对象(参见JDBC一章)。 它通常都封装了 CCI 的API:将应用级的输入对象传入到操作对象, 从而它能创建输入record然后转换接收到的record数据到一个应用级输出对象并返回它。

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

21.4.1. MappingRecordOperation

MappingRecordOperation 本质上与 CciTemplate 做的事情是一样的, 但是它表达了一个明确的、预配置(pre-configured)的操作作为对象。它提供了两个模板方法来指明如何将一个输入对象转换为输入记录, 以及如何将一个输出记录转换为输出对象(记录映射):

  • createInputRecord(..) 指定了如何将一个输入对象转换为输入Record

  • 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方法,传递一个应用级(application-level) 输入对象,并接收一个应用级输出对象作为结果:

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

21.4.2. MappingCommAreaOperation

一些连接器使用了基于COMMAREA的记录,该记录包含了发送给EIS的参数和返回数据的字节数组。 Spring提供了一个专门的操作类用于直接操作COMMAREA而不是操作记录。 MappingCommAreaOperation 类扩展了 MappingRecordOperation 类以提供这种专门的COMMAREA支持。 它隐含地使用了 CommAreaRecord类作为输入和输出record类型, 并提供了两个新的方法来转换输入对象到输入COMMAREA,以及转换输出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;
 ...
}

21.4.3. 自动生成输出记录

由于每个 MappingRecordOperation 子类的内部都是基于 CciTemplate 的,所以 用 CciTemplate 以相同的方式自动生成输出record都是有效的。 每个操作对象提供一个相应的 setOutputRecordCreator(..) 方法。 更多的信息,请参见前面的第 21.3.4 节 “自动输出记录生成”一节。

21.4.4. 总结

操作对象方法使用了跟 CciTemplate 相同的方式来使用记录。

表 21.2. Usage of Interaction execute methods

MappingRecordOperation method signature MappingRecordOperation outputRecordCreator property execute method called on the CCI Interaction
Object execute(Object) not set Record execute(InteractionSpec, Record)
Object execute(Object) set boolean execute(InteractionSpec, Record, Record)

21.4.5. MappingRecordOperation 使用示例

在本节中,将通过展示使用 Blackbox CCI 连接器访问一个数据库来说明 MappingRecordOperation 的用法。

注意

该连接器的最初版本是由SUN提供的J2EE SDK(1.3版本)。

首先,必须在 CCI InteractionSpec 中进行一些初始化动作来指定执行哪些SQL请求。 在这个例子中,我们直接定义了将请求参数转换为CCI record以及将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;
 }
}

然后应用程序会以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 beans的配置看起来类似于下面非托管模式(non-managed mode)的配置:

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

在托管模式(managed mode)(也就是说,在一个J2EE环境中)),配置可能看起来像这样:

<bean id="targetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
 <property name="jndiName" value="eis/blackbox"/>
</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>

21.4.6. MappingCommAreaOperation 使用示例

在本节中,将展示 MappingCommAreaOperation类的用法:通过IBM 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();
  }
 }
}

The abstract EciMappingOperation class can then be subclassed to specify mappings between custom objects and Records.

EciMappingOperation 抽象类可以被子类化以指定自定义对象和 Records 之间的映射。

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 beans的配置看起来类似于下面非托管模式(non-managed mode)的配置:

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

在托管模式(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>