キャッシュ・ストアは、データベースなどの外部ストアとの間でキャッシュ・エントリの読取りおよび書込みを行うために、キャッシュによって使用されます。このページの例では、キャッシュ・ストアの操作に使用できるいくつかの方法を紹介します。
|
注意: キャッシュをバルク・ロードすることによって、処理作業を軽減できます。次の例では、putメソッドを使用して、キャッシュ・ストアに値を書き込みます。多くの場合、putAllメソッドを使用してバルク・ロードを実行すると、処理作業およびネットワーク・トラフィックが軽減されます。バルク・ロードの詳細は、第12章「キャッシュの事前ロード」を参照してください。 |
この項では、com.tangosol.net.cache.CacheStoreインタフェースの非常に基本的な実装について説明します。例G-1の実装では、JDBCによる単一のデータベース接続を使用し、バルク操作は使用しません。完全な実装では接続プールが使用されます。また、ライトビハインドが使用される場合は、CacheStore.storeAll()を実装してJDBCのバルク挿入およびバルク更新を行います。データベースのキャッシュ・コンフィギュレーションのサンプルについては、「データベースのキャッシュ」を参照してください。
例G-1 CacheStoreインタフェースの実装
package com.tangosol.examples.coherence;
import com.tangosol.net.cache.CacheStore;
import com.tangosol.util.Base;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* An example implementation of CacheStore
* interface.
*
* @author erm 2003.05.01
*/
public class DBCacheStore
extends Base
implements CacheStore
{
// ----- constructors ---------------------------------------------------
/**
* Constructs DBCacheStore for a given database table.
*
* @param sTableName the db table name
*/
public DBCacheStore(String sTableName)
{
m_sTableName = sTableName;
configureConnection();
}
/**
* Set up the DB connection.
*/
protected void configureConnection()
{
try
{
Class.forName("org.gjt.mm.mysql.Driver");
m_con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
m_con.setAutoCommit(true);
}
catch (Exception e)
{
throw ensureRuntimeException(e, "Connection failed");
}
}
// ---- accessors -------------------------------------------------------
/**
* Obtain the name of the table this CacheStore is persisting to.
*
* @return the name of the table this CacheStore is persisting to
*/
public String getTableName()
{
return m_sTableName;
}
/**
* Obtain the connection being used to connect to the database.
*
* @return the connection used to connect to the database
*/
public Connection getConnection()
{
return m_con;
}
// ----- CacheStore Interface --------------------------------------------
/**
* Return the value associated with the specified key, or null if the
* key does not have an associated value in the underlying store.
*
* @param oKey key whose associated value is to be returned
*
* @return the value associated with the specified key, or
* <tt>null</tt> if no value is available for that key
*/
public Object load(Object oKey)
{
Object oValue = null;
Connection con = getConnection();
String sSQL = "SELECT id, value FROM " + getTableName()
+ " WHERE id = ?";
try
{
PreparedStatement stmt = con.prepareStatement(sSQL);
stmt.setString(1, String.valueOf(oKey));
ResultSet rslt = stmt.executeQuery();
if (rslt.next())
{
oValue = rslt.getString(2);
if (rslt.next())
{
throw new SQLException("Not a unique key: " + oKey);
}
}
stmt.close();
}
catch (SQLException e)
{
throw ensureRuntimeException(e, "Load failed: key=" + oKey);
}
return oValue;
}
/**
* Store the specified value under the specific key in the underlying
* store. This method is intended to support both key/value creation
* and value update for a specific key.
*
* @param oKey key to store the value under
* @param oValue value to be stored
*
* @throws UnsupportedOperationException if this implementation or the
* underlying store is read-only
*/
public void store(Object oKey, Object oValue)
{
Connection con = getConnection();
String sTable = getTableName();
String sSQL;
// the following is very inefficient; it is recommended to use DB
// specific functionality that is, REPLACE for MySQL or MERGE for Oracle
if (load(oKey) != null)
{
// key exists - update
sSQL = "UPDATE " + sTable + " SET value = ? where id = ?";
}
else
{
// new key - insert
sSQL = "INSERT INTO " + sTable + " (value, id) VALUES (?,?)";
}
try
{
PreparedStatement stmt = con.prepareStatement(sSQL);
int i = 0;
stmt.setString(++i, String.valueOf(oValue));
stmt.setString(++i, String.valueOf(oKey));
stmt.executeUpdate();
stmt.close();
}
catch (SQLException e)
{
throw ensureRuntimeException(e, "Store failed: key=" + oKey);
}
}
/**
* Remove the specified key from the underlying store if present.
*
* @param oKey key whose mapping is to be removed from the map
*
* @throws UnsupportedOperationException if this implementation or the
* underlying store is read-only
*/
public void erase(Object oKey)
{
Connection con = getConnection();
String sSQL = "DELETE FROM " + getTableName() + " WHERE id=?";
try
{
PreparedStatement stmt = con.prepareStatement(sSQL);
stmt.setString(1, String.valueOf(oKey));
stmt.executeUpdate();
stmt.close();
}
catch (SQLException e)
{
throw ensureRuntimeException(e, "Erase failed: key=" + oKey);
}
}
/**
* Remove the specified keys from the underlying store if present.
*
* @param colKeys keys whose mappings are being removed from the cache
*
* @throws UnsupportedOperationException if this implementation or the
* underlying store is read-only
*/
public void eraseAll(Collection colKeys)
{
throw new UnsupportedOperationException();
}
/**
* Return the values associated with each the specified keys in the
* passed collection. If a key does not have an associated value in
* the underlying store, then the return map will not have an entry
* for that key.
*
* @param colKeys a collection of keys to load
*
* @return a Map of keys to associated values for the specified keys
*/
public Map loadAll(Collection colKeys)
{
throw new UnsupportedOperationException();
}
/**
* Store the specified values under the specified keys in the underlying
* store. This method is intended to support both key/value creation
* and value update for the specified keys.
*
* @param mapEntries a Map of any number of keys and values to store
*
* @throws UnsupportedOperationException if this implementation or the
* underlying store is read-only
*/
public void storeAll(Map mapEntries)
{
throw new UnsupportedOperationException();
}
/**
* Iterate all keys in the underlying store.
*
* @return a read-only iterator of the keys in the underlying store
*/
public Iterator keys()
{
Connection con = getConnection();
String sSQL = "SELECT id FROM " + getTableName();
List list = new LinkedList();
try
{
PreparedStatement stmt = con.prepareStatement(sSQL);
ResultSet rslt = stmt.executeQuery();
while (rslt.next())
{
Object oKey = rslt.getString(1);
list.add(oKey);
}
stmt.close();
}
catch (SQLException e)
{
throw ensureRuntimeException(e, "Iterator failed");
}
return list.iterator();
}
// ----- data members ---------------------------------------------------
/**
* The connection.
*/
protected Connection m_con;
/**
* The db table name.
*/
protected String m_sTableName;
/**
* Driver class name.
*/
private static final String DB_DRIVER = "org.gjt.mm.mysql.Driver";
/**
* Connection URL.
*/
private static final String DB_URL = "jdbc:mysql://localhost:3306/CacheStore";
/**
* User name.
*/
private static final String DB_USERNAME = "root";
/**
* Password.
*/
private static final String DB_PASSWORD = null;
}
この項では、制御可能なキャッシュ・ストアの実装を紹介します。このシナリオでは、キャッシュ内で更新された値をデータ・ストアに書き込む時点をアプリケーションによって制御できます。このシナリオは、起動時にデータ・ストアからキャッシュへの初期移入を行う際に最も一般的に使用されます。起動時には、キャッシュの値を元のデータ・ストアに書き込む必要がありません。このような書込みはリソースの浪費になります。
例G-2のMain.javaファイルは、制御可能なキャッシュ・ストアの次の2種類の操作方法を示しています。
制御可能なキャッシュ(別のサービスに配置されている必要があります)を使用して、キャッシュ・ストアの有効化または無効化を行います。これは、ControllableCacheStore1クラスで示されます。
CacheStoreAwareインタフェースを使用して、キャッシュに追加されるオブジェクトを格納する必要がないことを示します。これは、ControllableCacheStore2クラスで示されます。
ControllableCacheStore1とControllableCacheStore2は両方とも、com.tangosol.net.cache.AbstractCacheStoreクラスを拡張したものです。このヘルパー・クラスによって、storeAll操作およびeraseAll操作の非最適実装が行われます。
CacheStoreAware.javaファイルは、キャッシュに追加されるオブジェクトをデータベースに格納しないことを示す場合に使用できるインタフェースです。
キャッシュ・コンフィギュレーションのサンプルについては、「データベースのキャッシュ」を参照してください。
例G-2には、Main.javaインタフェースが一覧表示されています。
例G-2 Main.java: 制御可能なCacheStoreとの相互作用
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.net.cache.AbstractCacheStore;
import com.tangosol.util.Base;
import java.io.Serializable;
import java.util.Date;
public class Main extends Base
{
/**
* CacheStore implementation which is controlled by a control cache
*/
public static class ControllableCacheStore1 extends AbstractCacheStore
{
public static final String CONTROL_CACHE = "cachestorecontrol";
String m_sName;
public static void enable(String sName)
{
CacheFactory.getCache(CONTROL_CACHE).put(sName, Boolean.TRUE);
}
public static void disable(String sName)
{
CacheFactory.getCache(CONTROL_CACHE).put(sName, Boolean.FALSE);
}
public void store(Object oKey, Object oValue)
{
Boolean isEnabled = (Boolean) CacheFactory.getCache(CONTROL_CACHE).get(m_sName);
if (isEnabled != null && isEnabled.booleanValue())
{
log("controllablecachestore1: enabled " + oKey + " = " + oValue);
}
else
{
log("controllablecachestore1: disabled " + oKey + " = " + oValue);
}
}
public Object load(Object oKey)
{
log("controllablecachestore1: load:" + oKey);
return new MyValue1(oKey);
}
public ControllableCacheStore1(String sName)
{
m_sName = sName;
}
}
/**
* CacheStore implementation which is controlled by values
* implementing the CacheStoreAware interface
*/
public static class ControllableCacheStore2 extends AbstractCacheStore
{
public void store(Object oKey, Object oValue)
{
boolean isEnabled = oValue instanceof CacheStoreAware ? !((CacheStoreAware) oValue).isSkipStore() : true;
if (isEnabled)
{
log("controllablecachestore2: enabled " + oKey + " = " + oValue);
}
else
{
log("controllablecachestore2: disabled " + oKey + " = " + oValue);
}
}
public Object load(Object oKey)
{
log("controllablecachestore2: load:" + oKey);
return new MyValue2(oKey);
}
}
public static class MyValue1 implements Serializable
{
String m_sValue;
public String getValue()
{
return m_sValue;
}
public String toString()
{
return "MyValue1[" + getValue() + "]";
}
public MyValue1(Object obj)
{
m_sValue = "value:" + obj;
}
}
public static class MyValue2 extends MyValue1 implements CacheStoreAware
{
boolean m_isSkipStore = false;
public boolean isSkipStore()
{
return m_isSkipStore;
}
public void skipStore()
{
m_isSkipStore = true;
}
public String toString()
{
return "MyValue2[" + getValue() + "]";
}
public MyValue2(Object obj)
{
super(obj);
}
}
public static void main(String[] args)
{
try
{
// example 1
NamedCache cache1 = CacheFactory.getCache("cache1");
// disable cachestore
ControllableCacheStore1.disable("cache1");
for(int i = 0; i < 5; i++)
{
cache1.put(new Integer(i), new MyValue1(new Date()));
}
// enable cachestore
ControllableCacheStore1.enable("cache1");
for(int i = 0; i < 5; i++)
{
cache1.put(new Integer(i), new MyValue1(new Date()));
}
// example 2
NamedCache cache2 = CacheFactory.getCache("cache2");
// add some values with cachestore disabled
for(int i = 0; i < 5; i++)
{
MyValue2 value = new MyValue2(new Date());
value.skipStore();
cache2.put(new Integer(i), value);
}
// add some values with cachestore enabled
for(int i = 0; i < 5; i++)
{
cache2.put(new Integer(i), new MyValue2(new Date()));
}
}
catch(Throwable oops)
{
err(oops);
}
finally
{
CacheFactory.shutdown();
}
}
}
例G-3には、CacheStoreAware.javaインタフェースが一覧表示されています。