エンティティー・マネージャーは、すべてのエンティティー・オブジェクトをタプル・オブジェクトに変換してから、WebSphere® eXtreme Scale マップに保管します。どのエンティティーにもキー・タプルと値タプルがあります。 このキーと値のペアは、エンティティーの関連 eXtreme Scale マップに保管されます。eXtreme Scale マップをローダーと共に使用する場合、ローダーは、タプル・オブジェクトと対話する必要があります。
eXtreme Scale には、リレーショナル・データベースとの統合を簡素化する Loader プラグインが含まれています。Java Persistence API (JPA) ローダーは、Java Persistence API を使用して、データベースと対話し、エンティティー・オブジェクトを作成します。 この JPA ローダーは、eXtreme Scale エンティティーと互換性があります。
タプルには、エンティティーの属性およびアソシエーションに関する情報が入っています。プリミティブ値は、プリミティブ・ラッパーを使用して保管されます。他のサポートされるオブジェクト・タイプは、そのネイティブ・フォーマットで保管されます。 他のエンティティーに対するアソシエーションは、ターゲット・エンティティーのキーを表すキー・タプル・オブジェクトのコレクションとして保管されます。
各属性またはアソシエーションは、ゼロ・ベース索引を使用して保管されます。各属性の索引を getAttributePosition メソッド、または getAssociationPosition メソッドを使用して取得できます。 位置が取得されると、その位置は eXtreme Scale ライフサイクルの実行期間中は変更されません。位置が変更 される可能性があるのは、eXtreme Scale が再始動 されるときです。タプルのエレメントの更新には、setAttribute メソッド、setAssociation メソッド、および setAssociations メソッドが使用されます。
以下の例では、タプルの処理方法について詳しく説明します。 この例の場合のエンティティーの定義について詳しくは、エンティティー・マネージャーのチュートリアル: Order エンティティー・スキーマを参照してください。 WebSphere eXtreme Scale は各エンティティーでローダーを使用するよう構成されています。また、取得されるのは Order エンティティーのみで、この特定のエンティティーは Customer エンティティーと多対 1 のリレーションシップを保有しています。 属性名は customer で、これは OrderLine エンティティーと 1 対多のリレーションシップを保有しています。
プロジェクターを使用して、エンティティーから自動的にタプル・オブジェクトを作成します。プロジェクターを使用すると、Hibernate や JPA などのオブジェクト関係マッピング・ユーティリティーを使用する場合にローダーを簡素化することができます。
@Entity
public class Order
{
@Id String orderNumber;
java.util.Date date;
@OneToOne(cascade=CascadeType.PERSIST) Customer customer;
@OneToMany(cascade=CascadeType.ALL, mappedBy="order") @OrderBy("lineNumber") List<OrderLine> lines;
}
@Entity
public class Customer {
@Id String id;
String firstName;
String surname;
String address;
String phoneNumber;
}
@Entity
public class OrderLine
{
@Id @ManyToOne(cascade=CascadeType.PERSIST) Order order;
@Id int lineNumber;
@OneToOne(cascade=CascadeType.PERSIST) Item item;
int quantity;
double price;
}
Loader インターフェースを実装する OrderLoader クラス を以下のコードに示します。以下の例では、関連の TransactionCallback プラグインが定義されているものとします。
public class OrderLoader implements com.ibm.websphere.objectgrid.plugins.Loader {
private EntityMetadata entityMetaData;
public void batchUpdate(TxID txid, LogSequence sequence)
throws LoaderException, OptimisticCollisionException {
...
}
public List get(TxID txid, List keyList, boolean forUpdate)
throws LoaderException {
...
}
public void preloadMap(Session session, BackingMap backingMap)
throws LoaderException {
this.entityMetaData=backingMap.getEntityMetadata();
}
}
eXtreme Scale からの preLoadMap メソッド呼び出し中に、インスタンス変数 entityMetaData が初期化されます。エンティティーを使用するようにマップが構成されている場合、entityMetaData 変数はヌルにはなりません。それ以外の場合、値は NULL です。
batchUpdate メソッドを使用することで、アプリケーションがどのアクションを実行しようとしているかを知ることができます。挿入、更新、または削除操作に基づいて、 データベースへの接続がオープンされ、作業が実行されます。キーと値のタイプは Tuple のため、これらを SQL ステートメントで意味を成す値に変換する必要があります。
以下のコードに示されているように、ORDER テーブルは、以下のデータ定義言語 (DDL) 定義 を使用して作成されました。
CREATE TABLE ORDER (ORDERNUMBER VARCHAR(250) NOT NULL, DATE TIMESTAMP, CUSTOMER_ID VARCHAR(250))
ALTER TABLE ORDER ADD CONSTRAINT PK_ORDER PRIMARY KEY (ORDERNUMBER)
以下のコードは、Tuple を Object に変換する方法を示しています。
public void batchUpdate(TxID txid, LogSequence sequence)
throws LoaderException, OptimisticCollisionException {
Iterator iter = sequence.getPendingChanges();
while (iter.hasNext()) {
LogElement logElement = (LogElement) iter.next();
Object key = logElement.getKey();
Object value = logElement.getCurrentValue();
switch (logElement.getType().getCode()) {
case LogElement.CODE_INSERT:
1) if (entityMetaData!=null) {
// The order has just one key orderNumber
2) String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
// Get the value of date
3) java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
// The values are 2 associations. Lets process customer because
// the our table contains customer.id as primary key
4) Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);
//Order to Customer is M to 1. There can only be 1 key
5) String CUSTOMER_ID=(String)keys[0];
// parse variable unFormattedDate and format it for the database as formattedDate
6) String formattedDate = "2007-05-08-14.01.59.780272"; // formatted for DB2
// Finally, the following SQL statement to insert the record
7) //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
}
break;
case LogElement.CODE_UPDATE:
break;
case LogElement.CODE_DELETE:
break;
}
}
}
// returns the value to attribute as stored in the key Tuple
private Object getKeyAttribute(String attr, Tuple key) {
//get key metadata
TupleMetadata keyMD = entityMetaData.getKeyMetadata();
//get position of the attribute
int keyAt = keyMD.getAttributePosition(attr);
if (keyAt > -1) {
return key.getAttribute(keyAt);
} else { // attribute undefined
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// returns the value to attribute as stored in the value Tuple
private Object getValueAttribute(String attr, Tuple value) {
//similar to above, except we work with value metadata instead
TupleMetadata valueMD = entityMetaData.getValueMetadata();
int keyAt = valueMD.getAttributePosition(attr);
if (keyAt > -1) {
return value.getAttribute(keyAt);
} else {
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// returns an array of keys that refer to association.
private Object[] getForeignKeyForValueAssociation(String attr, String fk_attr, Tuple value) {
TupleMetadata valueMD = entityMetaData.getValueMetadata();
Object[] ro;
int customerAssociation = valueMD.getAssociationPosition(attr);
TupleAssociation tupleAssociation = valueMD.getAssociation(customerAssociation);
EntityMetadata targetEntityMetaData = tupleAssociation.getTargetEntityMetadata();
Tuple[] customerKeyTuple = ((Tuple) value).getAssociations(customerAssociation);
int numberOfKeys = customerKeyTuple.length;
ro = new Object[numberOfKeys];
TupleMetadata keyMD = targetEntityMetaData.getKeyMetadata();
int keyAt = keyMD.getAttributePosition(fk_attr);
if (keyAt < 0) {
throw new IllegalArgumentException("Invalid position index for " + attr);
}
for (int i = 0; i < numberOfKeys; ++i) {
ro[i] = customerKeyTuple[i].getAttribute(keyAt);
}
return ro;
}
トランザクション区分およびデータベースへのアクセスは、ローダーの作成で取り上げています。
キャッシュ内でキーが検出されなかった場合は、Loader プラグインの get メソッドを呼び出し、キーを検出します。
public List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException {
System.out.println("OrderLoader: Get called");
ArrayList returnList = new ArrayList();
1) if (entityMetaData != null) {
int index=0;
for (Iterator iter = keyList.iterator(); iter.hasNext();) {
2) Tuple orderKeyTuple=(Tuple) iter.next();
// The order has just one key orderNumber
3) String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
//We need to run a query to get values of
4) // SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY'
5) //1) Foreign key: CUSTOMER_ID
6) //2) date
// Assuming those two are returned as
7) String CUSTOMER_ID = "C001"; // Assuming Retrieved and initialized
8) java.util.Date retrievedDate = new java.util.Date();
// Assuming this date reflects the one in database
// We now need to convert this data into a tuple before returning
//create a value tuple
9) TupleMetadata valueMD = entityMetaData.getValueMetadata();
Tuple valueTuple=valueMD.createTuple();
//add retrievedDate object to Tuple
int datePosition = valueMD.getAttributePosition("date");
10) valueTuple.setAttribute(datePosition, retrievedDate);
//Next need to add the Association
11) int customerPosition=valueMD.getAssociationPosition("customer");
TupleAssociation customerTupleAssociation =
valueMD.getAssociation(customerPosition);
EntityMetadata customerEMD = customerTupleAssociation.getTargetEntityMetadata();
TupleMetadata customerTupleMDForKEY=customerEMD.getKeyMetadata();
12) int customerKeyAt=customerTupleMDForKEY.getAttributePosition("id");
Tuple customerKeyTuple=customerTupleMDForKEY.createTuple();
customerKeyTuple.setAttribute(customerKeyAt, CUSTOMER_ID);
13) valueTuple.addAssociationKeys(customerPosition, new Tuple[] {customerKeyTuple});
14) int linesPosition = valueMD.getAssociationPosition("lines");
TupleAssociation linesTupleAssociation = valueMD.getAssociation(linesPosition);
EntityMetadata orderLineEMD = linesTupleAssociation.getTargetEntityMetadata();
TupleMetadata orderLineTupleMDForKEY = orderLineEMD.getKeyMetadata();
int lineNumberAt = orderLineTupleMDForKEY.getAttributePosition("lineNumber");
int orderAt = orderLineTupleMDForKEY.getAssociationPosition("order");
if (lineNumberAt < 0 || orderAt < 0) {
throw new IllegalArgumentException(
"Invalid position index for lineNumber or order "+
lineNumberAt + " " + orderAt);
}
15) // SELECT LINENUMBER FROM ORDERLINE WHERE ORDERNUMBER='ORDERNUMBERKEY'
// Assuming two rows of line number are returned with values 1 and 2
Tuple orderLineKeyTuple1 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple1.setAttribute(lineNumberAt, new Integer(1));// set Key
orderLineKeyTuple1.addAssociationKey(orderAt, orderKeyTuple);
Tuple orderLineKeyTuple2 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple2.setAttribute(lineNumberAt, new Integer(2));// Init Key
orderLineKeyTuple2.addAssociationKey(orderAt, orderKeyTuple);
16) valueTuple.addAssociationKeys(linesPosition, new Tuple[]
{orderLineKeyTuple1, orderLineKeyTuple2 });
returnList.add(index, valueTuple);
index++;
}
}else {
// does not support tuples
}
return returnList;
}
このトピックには、タプルの作成手順、および Order エンティティーの説明のみが含まれています。他のエンティティーや TransactionCallback プラグインと結び付けられているプロセス全体に対しても同様の手順を実行してください。詳しくは、トランザクションのライフサイクル・イベントの管理のためのプラグインを参照してください。