この演習では、テキスト・ファイルから読み取ったドメイン・オブジェクトをCoherenceキャッシュに移入する方法を学習します。ここでは、Coherenceのaggregator
、extractor
およびfilter
の各パッケージからAPIを使用することについて取り上げます。
この章には次の項が含まれます:
これまで、オブジェクトをキャッシュに格納したりキャッシュから取得したりする場合には、個別に実行してきました。この方法では、特にパーティション化されたりレプリケートされたりしたキャッシュで、put
メソッドをコールするたびにネットワーク・トラフィックが増加することがあります。また、put
メソッドをコールするたびに、キャッシュ内で置き換えられたばかりのオブジェクトが返され、不要なオーバーヘッドがさらに増えます。putAll
メソッドを使用すると、キャッシュのロードを大幅に効率化できます。
この章のタスクを実行するには、最初に第4章「複合オブジェクトの操作」で説明しているプロジェクトを完了する必要があります。また、テキスト・ファイルを読み取るにはjava.io.BufferedReader
、テキスト・ファイルを解析するにはjava.lang.String.split
メソッド、日付を解析するにはjava.text.SimpleDateFormat
の使用について理解しておく必要があります。
この演習では、Coherenceキャッシュにドメイン・オブジェクトを移入するコンソール・アプリケーションを作成する方法を示します。このアプリケーションはCoherenceのcom.tangosol.io.pof.PortableObject
実装を使用して、Portable Object format (POF)にオブジェクトをシリアライズします。
この演習では、Contact
オブジェクトを取得できるキー、キャッシュにデータを提供するジェネレータおよびキャッシュをロードするローダーを作成します。
ドメイン・オブジェクトのキーを持つクラスを作成するには:
Loading
という名前の新規アプリケーション・クライアント・プロジェクトを作成します。「Configuration」ドロップダウン・リストから「CoherenceConfig」を選択します。「New Application Client Project」ウィザードの「Application Client Module」ページで、「Create a default Main class」チェック・ボックスの選択を解除します。
新規プロジェクトの作成の詳細は、「複合オブジェクトの作成とキャッシュ」を参照してください。
前の演習(Contacts
)で作成したAddress
クラス、PhoneNumber
クラスおよびContact
クラスに関連するクラスとファイルを追加します。これらのファイルは、c:\home\oracle\workspace\Contacts\appClientModule
ディレクトリおよびc:\home\oracle\workspace\Contacts\build\classes
ディレクトリにあります。
「Project Explorer」でLoadingプロジェクトを右クリックして、「Properties」を選択します。「Properties for Loading」ダイアログ・ボックスで、「Java Build Path」を選択します。「Projects」タブで、「Add」をクリックします。図5-1に示されているように、「Required Project Selection」ダイアログ・ボックスから「Contacts」プロジェクトを選択します。
「Order and Export」タブで、「Up」ボタンと「Down」ボタンを使用して「Contacts」をリストの先頭に移動します。タブの内容は図5-2のようになります。
情報追跡の対象となる従業員のキーを提供するContactId
クラスを作成します。Javaクラスの作成の詳細は、「Javaクラスの作成」を参照してください。
従業員の姓名に基づいた連絡先IDを作成します。このオブジェクトは、Contact
オブジェクトを取得するキーとして機能します。
このクラスはPOFシリアライズを使用するので、PortableObject
インタフェース、writeExternal
メソッドとreadExternal
PortableObject
メソッドのほか、equals
オブジェクト、hashCode
オブジェクトおよびtoString
オブジェクトのメソッドを実装する必要があります。
注意: キャッシュのキーと値は、シリアライズ可能(たとえば、 |
例5-1は、考えられるContactId
クラスの実装を示しています。
例5-1 単純な連絡先IDクラス
package com.oracle.handson; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; import com.tangosol.util.Base; import com.tangosol.util.HashHelper; import java.io.IOException; /** * ContactId is a key to the person for whom information is * tracked. */ public class ContactId implements PortableObject { // ----- constructors --------------------------------------------------- /** * Default constructor (necessary for PortableObject implementation). */ public ContactId() { } /** * Construct a contact person. * */ public ContactId(String FirstName, String LastName) { super(); this.FirstName = FirstName; this.LastName = LastName; } // ----- accessors ------------------------------------------------------ /** * Return the first name. * */ public String getFirstName() { return FirstName; } /** * Return the last name. * */ public String getLastName() { return LastName; } // ----- PortableObject interface --------------------------------------- public void readExternal(PofReader reader) throws IOException { FirstName = reader.readString(0); LastName = reader.readString(1); } public void writeExternal(PofWriter writer) throws IOException { writer.writeString(0, FirstName); writer.writeString(1, LastName); } // ----- Object methods ------------------------------------------------- public boolean equals(Object oThat) { if (this == oThat) { return true; } if (oThat == null) { return false; } ContactId that = (ContactId) oThat; return Base.equals(getFirstName(), that.getFirstName()) && Base.equals(getLastName(), that.getLastName()); } public int hashCode() { return HashHelper.hash(getFirstName(), HashHelper.hash(getLastName(), 0)); } public String toString() { return getFirstName() + " " + getLastName(); } // ----- data members --------------------------------------------------- /** * First name. */ private String FirstName; /** * Last name. */ private String LastName; }
POF構成ファイルを編集します。ContactId
クラスの<user-type>
エントリをcontacts-pof-config.xml
ファイルに追加します。ファイルは例5-2のようになります。
例5-2 ContactIdエントリを持つPOF構成ファイル
<?xml version="1.0"?> <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config" xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd"> <user-type-list> <!-- coherence POF user types --> <include>coherence-pof-config.xml</include> <!-- com.tangosol.examples package --> <user-type> <type-id>1001</type-id> <class-name>com.oracle.handson.Contact</class-name> </user-type> <user-type> <type-id>1002</type-id> <class-name>com.oracle.handson.Address</class-name> </user-type> <user-type> <type-id>1003</type-id> <class-name>com.oracle.handson.PhoneNumber</class-name> </user-type> <user-type> <type-id>1004</type-id> <class-name>com.oracle.handson.ContactId</class-name> </user-type> </user-type-list> <allow-interfaces>true</allow-interfaces> <allow-subclasses>true</allow-subclasses> </pof-config>
ランダムな従業員の連絡先の名前やアドレスを生成するDataGenerator
という名前のJavaクラスを作成します。詳細は、「Javaクラスの作成」を参照してください。
前の演習で作成したAddress
クラス、PhoneNumber
クラスおよびContact
クラスを使用します。ランダムな名前、アドレス、電話番号および年齢を生成するには、java.util.Random
を使用します。
例5-3は、考えられるデータ生成の実装を示しています。この実装では、従業員の連絡先情報が記載されたテキスト・ファイル(contacts.cvs
)を作成します。
例5-3 データ生成クラスのサンプル
package com.oracle.handson; import com.oracle.handson.Address; import com.oracle.handson.Contact; import com.oracle.handson.PhoneNumber; import com.tangosol.util.Base; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.sql.Date; import java.util.Collections; import java.util.Random; /** * DataGenerator is a generator of sample contacts. */ public class DataGenerator { // ----- static methods ------------------------------------------------- /** * Generate contacts. */ public static void main(String[] asArg) throws IOException { String sFile = asArg.length > 0 ? asArg[0] : FILENAME; int cCon = asArg.length > 1 ? Integer.parseInt(asArg[1]) : 1000; OutputStream out = new FileOutputStream(sFile); generate(out, cCon); out.close(); } /** * Generate the contacts and write them to a file. */ public static void generate(OutputStream out, int cContacts) throws IOException { PrintWriter writer = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out))); for (int i = 0; i < cContacts; ++i) { StringBuffer sb = new StringBuffer(256); //contact person sb.append("John,") .append(getRandomName()) .append(','); // home and work addresses sb.append(Integer.toString(Base.getRandom().nextInt(999))) .append(" Beacon St.,,") /*street1,empty street2*/ .append(getRandomName()) /*random city name*/ .append(',') .append(getRandomState()) .append(',') .append(getRandomZip()) .append(",US,Yoyodyne Propulsion Systems,") .append("330 Lectroid Rd.,Grover's Mill,") .append(getRandomState()) .append(',') .append(getRandomZip()) .append(",US,"); // home and work telephone numbers sb.append("home,") .append(Base.toDelimitedString(getRandomPhoneDigits(), ",")) .append(",work,") .append(Base.toDelimitedString(getRandomPhoneDigits(), ",")) .append(','); // random birth date in millis before or after the epoch sb.append(getRandomDateInMillis()); writer.println(sb); } writer.flush(); } /** * Return a random name. * */ private static String getRandomName() { Random rand = Base.getRandom(); int cCh = 4 + rand.nextInt(7); char[] ach = new char[cCh]; ach[0] = (char) ('A' + rand.nextInt(26)); for (int of = 1; of < cCh; ++of) { ach[of] = (char) ('a' + rand.nextInt(26)); } return new String(ach); } /** * Return a random phone muber. * The phone number includes access, country, area code, and local * number. * */ private static int[] getRandomPhoneDigits() { Random rand = Base.getRandom(); return new int[] { 11, // access code rand.nextInt(99), // country code rand.nextInt(999), // area code rand.nextInt(9999999) // local number }; } /** * Return a random Phone. * */ private static PhoneNumber getRandomPhone() { int[] anPhone = getRandomPhoneDigits(); return new PhoneNumber((short)anPhone[0], (short)anPhone[1], (short)anPhone[2], anPhone[3]); } /** * Return a random Zip code. * */ private static String getRandomZip() { return Base.toDecString(Base.getRandom().nextInt(99999), 5); } /** * Return a random state. * */ private static String getRandomState() { return STATE_CODES[Base.getRandom().nextInt(STATE_CODES.length)]; } /** * Return a random date in millis before or after the epoch. * */ private static long getRandomDateInMillis() { return (Base.getRandom().nextInt(40) - 20) * Contact.MILLIS_IN_YEAR; } /** * Generate a Contact with random information. * */ public static Contact getRandomContact() { return new Contact("John", getRandomName(), new Address("1500 Boylston St.", null, getRandomName(), getRandomState(), getRandomZip(), "US"), new Address("8 Yawkey Way", null, getRandomName(), getRandomState(), getRandomZip(), "US"), Collections.singletonMap("work", getRandomPhone()), new Date(getRandomDateInMillis())); } // ----- constants ------------------------------------------------------ /** * US Postal Service two letter postal codes. */ private static final String[] STATE_CODES = { "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "OF", "DC", "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR", "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI", "VA", "WA", "WV", "WI", "WY" }; /** * Default contacts file name. */ public static final String FILENAME = "contacts.csv"; }
DataGenerator
クラスは、従業員に関する情報をランダムに生成します。たとえば、従業員の名前、自宅の住所、勤務先の住所、自宅の電話番号、勤務先の電話番号および従業員の年齢などの情報です。情報は、contacts.csv
という名前のCSV形式のファイルに保存されます。このファイルはCSV形式のファイルの標準ルールに従っているため、1行ごとに1つのレコードが記載され、レコード内の個々のフィールドはカンマで区切られています。
例5-4は、contacts.csv
ファイルの最初の数エントリを示しています。
例5-4 contacts.csvファイルの内容
John,Dvcqbvcp,669 Beacon St.,,Tetuvusz,CA,68457,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,KS,30344,US,home,11,98,183,8707139,work,11,96,425,1175949,63072000000 John,Fxsr,549 Beacon St.,,Jutaswmaby,MI,16315,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,CT,60309,US,home,11,20,40,3662989,work,11,41,187,3148474,-189216000000 John,Gmyrolvfyd,73 Beacon St.,,Lpnztf,AR,82667,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,NY,42297,US,home,11,22,17,8579970,work,11,35,338,9286245,-567648000000 John,Efmpjlbj,85 Beacon St.,,Wyswpb,AK,29590,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,NJ,06219,US,home,11,20,744,4331451,work,11,39,911,9104590,-157680000000 ...
LoaderExample
という名前のJavaクラスを作成します。詳細は、「Javaクラスの作成」を参照してください。
クラスを実装して、「データ・ジェネレータの作成」の項で説明したプログラムで生成された従業員のデータを含むキャッシュをロードします。入力ストリームおよびバッファ済リーダーを使用して、contacts.csv
ファイルの従業員の情報を単一のCoherenceキャッシュにロードします。
データ・ファイルの従業員の情報を解析するコードを追加します。この情報を取得したら、個々の連絡先を作成してキャッシュに挿入します。処理作業を軽減し、ネットワーク・トラフィックを最低限に抑えるには、putAll
メソッドを使用してキャッシュをロードします。
例5-5は、考えられるLoaderExample
クラスの実装を示しています。
例5-5 キャッシュ・ロード・プログラムのサンプル
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import com.oracle.handson.ContactId; import com.oracle.handson.Address; import com.oracle.handson.PhoneNumber; import com.oracle.handson.Contact; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.sql.Date; import java.util.HashMap; import java.util.Map; /** * LoaderExample loads contacts into the cache from a file. * */ public class LoaderExample { // ----- static methods ------------------------------------------------- /** * Load contacts. */ public static void main (String[] asArg) throws IOException { String sFile = asArg.length > 0 ? asArg[0] : DataGenerator.FILENAME; String sCache = asArg.length > 1 ? asArg[1] : CACHENAME; System.out.println("input file: " + sFile); System.out.println("cache name: " + sCache); new LoaderExample().load(CacheFactory.getCache(sCache), new FileInputStream(sFile)); CacheFactory.shutdown(); } /** * Load cache from stream. * */ public void load(NamedCache cache, InputStream in) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); Map mapBatch = new HashMap(1024); String sRecord; int cRecord = 0; while ((sRecord = reader.readLine()) != null) { // parse record String[] asPart = sRecord.split(","); int ofPart = 0; String sFirstName = asPart[ofPart++]; String sLastName = asPart[ofPart++]; ContactId id = new ContactId(sFirstName, sLastName); Address addrHome = new Address( /*streetline1*/ asPart[ofPart++], /*streetline2*/ asPart[ofPart++], /*city*/ asPart[ofPart++], /*state*/ asPart[ofPart++], /*zip*/ asPart[ofPart++], /*country*/ asPart[ofPart++]); Address addrWork = new Address( /*streetline1*/ asPart[ofPart++], /*streetline2*/ asPart[ofPart++], /*city*/ asPart[ofPart++], /*state*/ asPart[ofPart++], /*zip*/ asPart[ofPart++], /*country*/ asPart[ofPart++]); Map mapTelNum = new HashMap(); for (int c = asPart.length - 1; ofPart < c; ) { mapTelNum.put(/*type*/ asPart[ofPart++], new PhoneNumber( /*access code*/ Short.parseShort(asPart[ofPart++]), /*country code*/ Short.parseShort(asPart[ofPart++]), /*area code*/ Short.parseShort(asPart[ofPart++]), /*local num*/ Integer.parseInt(asPart[ofPart++]))); } Date dtBirth = new Date(Long.parseLong(asPart[ofPart])); // Construct Contact and add to batch Contact con1 = new Contact(sFirstName, sLastName, addrHome, addrWork, mapTelNum, dtBirth); System.out.println(con1); mapBatch.put(id, con1); ++cRecord; if (cRecord % 1024 == 0) { // load batch cache.putAll(mapBatch); mapBatch.clear(); System.out.print('.'); System.out.flush(); } } if (!mapBatch.isEmpty()) { // load final batch cache.putAll(mapBatch); } System.out.println("Added " + cRecord + " entries to cache"); } // ----- constants ------------------------------------------------------ /** * Default cache name. */ public static final String CACHENAME = "ContactsCache"; }
キャッシュ・ロードのサンプルを実行するには:
実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
ContactsCacheServer
ファイルを使用して、キャッシュ・サーバーを起動します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択し、ContactsCacheServer
構成を編集します。「Main」タブで、「Browse」をクリックしてLoadingプロジェクトを選択します。
「Classpath」タブで、「User Entries」を選択して「Add Projects」をクリックします。「Project Selection」ダイアログ・ボックスで、Loadingプロジェクトを選択します。「OK」をクリックします。
「Common」タブで、「Shared file」をクリックしてLoadingプロジェクトを参照します。
「Apply」→「Run」をクリックします。
DataGenerator
の実行構成を作成します。「Project Explorer」のDataGenerator
を右クリックし、「Run As」を選択します。「Run Configurations」ダイアログ・ボックスで、「Oracle Coherence」を選択して「New Configuration」アイコンをクリックします。
「Name」フィールドにDataGenerator
と入力します。
「Main」タブの「Project」フィールドにLoading
と入力します。「Main class」フィールドにcom.oracle.handson.DataGenerator
と入力します。
「Coherence」タブの「Generalタブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\contacts-cache-config.xml
ファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155
と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xml
への絶対パスを入力します。「Apply」をクリックします。
「Common」タブで、「Shared file」を選択してLoadingディレクトリを参照します。
「Classpath」タブのLoadingの内容を確認します。図5-3のように、「Contacts」が「Loading」の下にエントリの1つとして表示されます。
LoaderExample
の実行構成を作成します。
「Name」フィールドにLoaderGenerator
と入力します。
「Main」タブの「Project」フィールドにLoading
と入力します。「Main class」フィールドにcom.oracle.handson.LoaderExample
と入力します。
「Coherence」タブの「Generalタブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\contacts-cache-config.xml
ファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155
と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xml
への絶対パスを入力します。「Apply」をクリックします。
「Classpath」タブの内容を確認します。図5-4のように、「Contacts」が「Loading」の下にエントリの1つとして表示されます。表示されない場合は、「User Entries」をクリックし、「Add Project」ボタンでプロジェクトを追加します。
「Run Configurations」ダイアログ・ボックスからDataGenerator
構成を実行します。次にLoaderExample
構成を実行します。
DataGenerator
実行構成によりデータ・ファイルが生成されます。Eclipseコンソールのウィンドウには出力や応答は表示されません。LoaderGenerator
実行構成により、例5-6のような従業員の連絡先情報のストリームがEclipseコンソール・ウィンドウに送信されます。
例5-6 サンプル・キャッシュ・ロード・プログラムの出力
... John Itqx Addresses Home: 58 Beacon St. Fcfz, NY 84297 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, RI 29443 US Telephone Numbers work: +11 93 148 8101744 home: +11 70 29 5079314 Birth Date: 1975-12-30 John Ysxydw Addresses Home: 623 Beacon St. Ohirim, PR 93378 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, FL 82251 US Telephone Numbers work: +11 5 799 6030449 home: +11 14 213 7774277 Birth Date: 1957-01-03 John Htmzlvoig Addresses Home: 700 Beacon St. Oqkqwbvnj, SC 45698 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, MI 74783 US Telephone Numbers work: +11 65 692 375834 home: +11 73 881 452999 Birth Date: 1973-12-30 Added 1000 entries to cache 2013-11-21 12:52:47.980/5.742 Oracle Coherence GE 12.1.3.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=n/a): Service PartitionedPofCache left the cluster ...
この演習では、キャッシュ内のデータの問合せおよび集計について紹介します。この演習では、次の方法を説明します。
キャッシュでの特定データの問合せ
キャッシュ内の情報の集計
名前を持つキャッシュに複合オブジェクトを挿入すると、グリッド内の情報を問い合せたり集計したりできます。com.tangosol.util.QueryMap
インタフェースにより、キャッシュ内の値やキーを管理できます。フィルタを使用して結果を制限できます。また、索引を定義して問合せを最適化することもできます。
Coherenceでは格納時に情報がシリアライズされるため、情報の問合せの際にデシリアライズによるオーバーヘッドも発生します。索引が追加されると、索引自体に保存されている値はデシリアライズされるため、アクセスが速くなります。索引付けされるオブジェクトは、常にシリアライズされています。
QueryMap
インタフェースには、よく使用される次のようなメソッドがあります。
Set
entrySet(Filterfilter)
は、マップ内のエントリのうちフィルタ条件を満たす一連のエントリを返します。
addIndex(ValueExtractorextractor,booleanfOrdered, Comparator comparator)
は、索引を追加します。
Set
keySet(Filter filter)
は、entrySet
に似ていますが、値ではなくキーを返します。
フィルタリングはキャッシュ・エントリ・オーナー・レベルで発生することに注意してください。パーティション化されたトポロジでは、フィルタリングを実行するのはプライマリ・パーティションであるため、フィルタリングは並行して実行できます。QueryMap
インタフェースでは、Filter
クラスが使用されます。これらのクラスの詳細は、com.tangosol.util.filter
パッケージのAPIを参照してください。
CoherenceのNamedCache
オブジェクトはすべて、com.tangosol.util.QueryMap
インタフェースを実装します。これにより、NamedCache
オブジェクトでは、キャッシュ内の指定された条件を満たすキーやエントリの検索がサポートされます。この条件は、com.tangosol.util.Filter
インタフェースを実装するオブジェクトとして表すことができます。
com.tangosol.util.filter
パッケージには、標準的な問合せ式を実装する事前定義された複数のクラスが含まれています。このようなクラスの例には、GreaterFilter
、GreaterEquals
、LikeFilter
、NotEqualsFilter
、InFilter
などがあります。これらのフィルタを使用して、大半のSQL WHERE
句式をオブジェクトベースで表現した式を構成できます。
注意: Coherenceには |
Filter
クラスは、標準的なJavaメソッド・リフレクションを使用してテスト条件を表現します。たとえば、次のフィルタは、キャッシュ内のオブジェクトでgetHomeAddress.getState
メソッドから返される値がMassachusetts (MA
)に対する値であるという条件を表しています。
(new EqualsFilter("getHomeAddress.getState", "MA");
このフィルタを使用してテストしたオブジェクトにget
メソッドが指定されていない場合、テストは失敗します。
Filter
クラスを使用したその他の例は、次のとおりです。
名前がS
で始まる都市に住む人のセットを返します。
Set sPeople = cache.entrySet(new LikeFilter("getHomeAddress.getCity","S%");
年齢が42歳を超える人のセットを返します。
final int nAge = 42;// Find all contacts who are older than nAge Set sSeniors = cache.entrySet(new GreaterFilter("getAge", nAge));
QueryMap
インタフェースで定義されたentrySet
メソッドとkeySet
メソッド以外に、CoherenceではaddIndex
メソッドを使用して索引を定義し、問合せのパフォーマンスを改善できます。よく知られている、厳密に適用された名前付き列のコレクション(スキーマ)に従って索引を定義する、リレーショナル・データベース・システムとは異なり、Coherenceにはスキーマがありません。スキーマがないため、従来のデータベースとは異なる方法で索引を定義します。
キャッシュに配置されている各オブジェクトに対する索引を付ける値を定義するために、Coherenceではvalue extractorの概念が導入されています。com.tangosol.util.ValueExtractor
インタフェースはextract
メソッドを定義します。オブジェクトのパラメータを指定すると、ValueExtractor
実装はパラメータに基づいて値を返します。
ValueExtractor
実装の簡単な例であるcom.tangosol.util.extractor.ReflectionExtractor
インタフェースでは、リフレクションを使用して、オブジェクトに対するメソッド・コールの結果を返します。たとえば、次のとおりです。
new ReflectionExtractor("getCity")
値エクストラクタは、Coherence API全体で使用できます。ただし通常は、索引を定義するために使用されます。
特に便利なエクストラクタのタイプは、ChainedExtractor
です。これは、エクストラクタの配列に基づく複合的なValueExtractor
実装です。配列内のエクストラクタは、左から右に順次適用されます。前のエクストラクタの結果が次のエクストラクタのターゲット・オブジェクトになります。たとえば、次のとおりです。
new ChainedExtractor(new ReflectionExtractor("getHomeAddress"), new ReflectionExtractor("getState"))
この例では、HomeAddress
オブジェクトとState
オブジェクトが複合Contact
オブジェクトに属していることが前提となっています。ChainedExtractor
は、まずリフレクションを使用して、キャッシュされた各Contact
オブジェクトにgetHomeAddress
メソッドをコールします。次に、リフレクションを使用して、返されたHomeAddress
オブジェクトのセットにgetState
メソッドをコールします。
問合せを実行するQueryExample
という新規Javaクラスを作成します。このクラスにmain
メソッドが存在することを確認します。詳細は、「Javaクラスの作成」を参照してください。
entrySet
メソッドを使用して、次のように従業員の連絡先情報を取得します。
マサチューセッツに住むすべての従業員:
cache.entrySet(new EqualsFilter("getHomeAddress.getState", "MA"));
マサチューセッツに住み、勤務先が別の場所であるすべての従業員:
cache.entrySet(new AndFilter( new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA")));
名前がS
で始まる都市に住むすべての従業員:
cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%"));
姓がS
で始まり、マサチューセッツに住むすべての従業員。問合せにキーと値の両方を使用します。キャッシュ・エントリでは、キーとしてContactId
オブジェクトが使用されます。KeyExtractor
を使用して、これらの値を取得できます。KeyExtractor
は値ではなく主要なオブジェクトに対して問合せを実行する必要があることを示す、特別な値エクストラクタ:
cache.entrySet(new AndFilter( new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA")));
指定された年齢を超えるすべての従業員。ヒント:
final int nAge = 42; setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
索引を使用して、パフォーマンスを向上させます。ヒント: QueryMap
インタフェースのJavadocでaddIndex
メソッドに関する記述を検索します。
例5-7は、考えられるQueryExample
クラスの実装を示しています。
例5-7 QueryExampleクラスのサンプル
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache; import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.KeyExtractor; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.AlwaysFilter; import com.tangosol.util.filter.AndFilter; import com.tangosol.util.filter.EqualsFilter; import com.tangosol.util.filter.GreaterFilter; import com.tangosol.util.filter.LikeFilter; import com.tangosol.util.filter.NotEqualsFilter; import java.util.Iterator; import java.util.Set; /** * QueryExample runs sample queries for contacts. * */ public class QueryExample{ // ----- QueryExample methods --------------------------------------- public static void main(String[] args) { NamedCache cache = CacheFactory.getCache("ContactsCache"); query(cache); } /** * Perform the example queries * */ public static void query(NamedCache cache) { // Add indexes to make queries more efficient ReflectionExtractor reflectAddrHome = new ReflectionExtractor("getHomeAddress"); // Add an index for the age cache.addIndex(new ReflectionExtractor("getAge"), true, null); // Add index for state within home address cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null); // Add index for state within work address cache.addIndex(new ChainedExtractor( new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null); // Add index for city within home address cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null); // Find all contacts who live in Massachusetts Set setResults = cache.entrySet(new EqualsFilter( "getHomeAddress.getState", "MA")); printResults("MA Residents", setResults); // Find all contacts who live in Massachusetts and work elsewhere setResults = cache.entrySet(new AndFilter( new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA"))); printResults("MA Residents, Work Elsewhere", setResults); // Find all contacts whose city name begins with 'S' setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%")); printResults("City Begins with S", setResults); final int nAge = 42; // Find all contacts who are older than nAge setResults = cache.entrySet(new GreaterFilter("getAge", nAge)); printResults("Age > " + nAge, setResults); // Find all contacts with last name beginning with 'S' that live // in Massachusetts. Uses both key and value in the query. setResults = cache.entrySet(new AndFilter( new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA"))); printResults("Last Name Begins with S and State Is MA", setResults); } /** * Print results of the query * * @param sTitle the title that describes the results * @param setResults a set of query results */ private static void printResults(String sTitle, Set setResults) { System.out.println(sTitle); for (Iterator iter = setResults.iterator(); iter.hasNext(); ) { System.out.println(iter.next()); } } }
問合せサンプルを実行するには:
すべての稼働しているキャッシュ・サーバーを停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
QueryExample
クラスに対する実行構成を作成します。
「Project Navigator」でQueryExample
を右クリックし、「Run As」→「Run Configurations」を選択します。「Oracle Coherence」を選択して、「New launch configuration」アイコンをクリックします。
「Name」フィールドにQueryExample
と入力します。
「Main」タブの「Project」フィールドにLoading
と入力します。「Main class」フィールドにcom.oracle.handson.QueryExample
と入力します。
「Coherence」タブの「Generalタブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\contacts-cache-config.xml
ファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155
と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xml
への絶対パスを入力します。「Apply」をクリックします。
「Classpath」タブの内容を確認します。Contactsプロジェクトが、「User Entries」のLoadingプロジェクトの下に表示されます。
Contacts
CacheServer
を再起動します。
DataGenerator
クラス、LoaderExample
クラスおよびQueryExample
クラスを実行します。
キャッシュ内のすべての連絡先情報を印刷すると、QueryExample
ファイルで問合せの結果が表示されます。結果は、例5-8のようになります。
例5-8 QueryExampleプログラムの結果
...MA Residents
ConverterEntry{Key="John Ueccc", Value="John Ueccc Addresses Home: 285 Beacon St. Oxtqwisgti, MA 41063 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, VA 28107 US Telephone Numbers work: +11 48 835 1876678 home: +11 78 482 1247744 Birth Date: 1972-12-30"} ...MA Residents, Work Elsewhere
ConverterEntry{Key="John Ueccc", Value="John Ueccc Addresses Home: 285 Beacon St. Oxtqwisgti, MA 41063 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, VA 28107 US Telephone Numbers work: +11 48 835 1876678 home: +11 78 482 1247744 Birth Date: 1972-12-30"} ...City Begins with S
ConverterEntry{Key="John Frepojf", Value="John Frepojf Addresses Home: 851 Beacon St. Swnsfng, PR 00734 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, AR 97794 US Telephone Numbers work: +11 10 474 781020 home: +11 29 575 9284939 Birth Date: 1985-12-27"} ...Age > 42
ConverterEntry{Key="John Qghbguyn", Value="John Qghbguyn Addresses Home: 49 Beacon St. Dvftzpq, PR 34220 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, MD 28247 US Telephone Numbers work: +11 39 563 7113013 home: +11 58 910 4667915 Birth Date: 1961-01-02"} ...Last Name Begins with S and State Is MA
ConverterEntry{Key="John Smnfg", Value="John Smnfg Addresses Home: 178 Beacon St. Hbpeak, MA 64081 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, OK 92191 US Telephone Numbers work: +11 56 322 7307404 home: +11 33 928 3075361 Birth Date: 1978-12-29"} ...
コードをQueryExample.java
ファイルに追加して、キャッシュ・データに対して集計を実行します。エントリ・アグリゲータ(com.tangosol.util.InvocableMap.EntryAggregator
)を使用すると、すべてのオブジェクトまたは特定のオブジェクトのセットに対して操作を実行し、集計を返すことができます。EntryAggregator
インタフェースは、クラスタ内のデータに対して並行してサービスを実行するエージェントです。集計は並行して実行され、クラスタ・メンバーの追加によるメリットを得ることができます。
集計には、キーのコレクションに対する集計と、フィルタの指定による集計の2つの方法があります。例5-9は、これらのタスクを実行するEntryAggregator
メソッドを示しています。
例5-9 キーに対する集計メソッドとフィルタ指定による集計メソッド
Object aggregate(Collection keys, InvocableMap.entryAggregator agg) Object aggregate(Filter filter, InvocableMap.entryAggregator agg)
フィルタリングに対してデータを返すために集計を追加するには:
集計を使用し、QueryExample
クラスにコードを記述して次の項目を計算します。
指定した年齢を超える従業員の数。GreaterFilter
クラスおよびCount
クラスを使用します。
cache.aggregate(new GreaterFilter("getAge", nAge), new Count())
従業員セット内の最低年齢。AlwaysFilter
クラスおよびLongMin
クラスを使用使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))
従業員セット内の最高年齢。AlwaysFilter
クラスおよびLongMax
クラスを使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))
従業員の平均年齢。AlwaysFilter
クラスおよびDoubleAverage
クラスを使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")
Count
、DoubleAverage
、LongMax
およびLongMin
アグリゲータ・クラスをインポートします。
import com.tangosol.util.aggregator.Count; import com.tangosol.util.aggregator.DoubleAverage; import com.tangosol.util.aggregator.LongMax; import com.tangosol.util.aggregator.LongMin;
QueryExample.java
ファイルは例5-10のようになります。
例5-10 集計を使用したQueryExample
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache;import com.tangosol.util.aggregator.Count;
import com.tangosol.util.aggregator.DoubleAverage;
import com.tangosol.util.aggregator.LongMax;
import com.tangosol.util.aggregator.LongMin;
import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.KeyExtractor; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.AlwaysFilter; import com.tangosol.util.filter.AndFilter; import com.tangosol.util.filter.EqualsFilter; import com.tangosol.util.filter.GreaterFilter; import com.tangosol.util.filter.LikeFilter; import com.tangosol.util.filter.NotEqualsFilter; import java.util.Iterator; import java.util.Set; /** * QueryExample runs sample queries for contacts. */ public class QueryExample{ // ----- QueryExample methods --------------------------------------- public static void main(String[] args) { NamedCache cache = CacheFactory.getCache("ContactsCache"); query(cache); } /** * Perform the example queries * */ public static void query(NamedCache cache) { // Add indexes to make queries more efficient ReflectionExtractor reflectAddrHome = new ReflectionExtractor("getHomeAddress"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor( new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null); // Find all contacts who live in Massachusetts Set setResults = cache.entrySet(new EqualsFilter( "getHomeAddress.getState", "MA")); printResults("MA Residents", setResults); // Find all contacts who live in Massachusetts and work elsewhere setResults = cache.entrySet(new AndFilter( new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA"))); printResults("MA Residents, Work Elsewhere", setResults); // Find all contacts whose city name begins with 'S' setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%")); printResults("City Begins with S", setResults); final int nAge = 42; // Find all contacts who are older than nAge setResults = cache.entrySet(new GreaterFilter("getAge", nAge)); printResults("Age > " + nAge, setResults); // Find all contacts with last name beginning with 'S' that live // in Massachusetts. Uses both key and value in the query. setResults = cache.entrySet(new AndFilter( new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA"))); printResults("Last Name Begins with S and State Is MA", setResults); // Count contacts who are older than nAge System.out.println("count > " + nAge + ": "+ cache.aggregate( new GreaterFilter("getAge", nAge), new Count())); // Find minimum age System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))); // Calculate average age System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge"))); // Find maximum age System.out.println("max age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))); } /** * Print results of the query * */ private static void printResults(String sTitle, Set setResults) { System.out.println(sTitle); for (Iterator iter = setResults.iterator(); iter.hasNext(); ) { System.out.println(iter.next()); } } }
キャッシュを問い合せて結果を集計するには:
Contacts
キャッシュ・サーバー、ContactsCacheServer
を停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
ContactsCacheServer
を再起動します。
DataGenerator
アプリケーション、LoaderExample
アプリケーションおよびQueryExample
アプリケーションを実行します。
Eclipseコンソールに、例5-11のように出力されます。
例5-11 アグリゲータからの出力
...Last Name Begins with S and State Is MA
ConverterEntry{Key="John Sqmyas", Value="John Sqmyas Addresses Home: 594 Beacon St. Jxaxt, MA 10081 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, NJ 95236 US Telephone Numbers work: +11 70 837 4723938 home: +11 2 227 6816592 Birth Date: 1977-12-29"}count > 42: 482
min age: 22
avg age: 41.627
max age: 61