#chiroito ’s blog

Java を中心とした趣味の技術について

CacheStoreにちょっとガッカリ

所用でCacheStoreを覚えないといけなかったのでちょっと見てみました。

参考:
Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching
CacheStore (Oracle® Coherence Java API Reference)

CoherenceにちょっとガッカリしたのはCacheStoreインターフェースが総称型を使っていないこと。
せっかくKeyとValueでやっているのだからとか対応して欲しいな。
implementするたびに警告が増えてしまう。
何かできない理由でもあるのだろうか。

サンプルソースへの個人的なダメ出しとしては
・インデントが合っていません。
・I/Fを実装したメソッドがどれか分かり難いです。
・使われてないメソッドが記載されています。
・計算する必要ないものを計算しています。
・不使用なフィールドがあります。
コンパイルすると警告が出ます。

ここからは人(またはコーディングルール)によってだと思います。
・メソッド名だけではstatic importとの区別がつきにくい
・一度しか使わない戻り値を変数に入れている。
・メソッドの下にフィールドあるのは違和感がある
・メソッド/フィールドのアクセス修飾子は適切に。

packageは変えています(笑)
package com.chirokings.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.Map;

/**
* An example implementation of CacheStore interface.
*
* @author Chihiro Ito 2011.01.08
*/
public class DBCacheStore extends Base implements CacheStore {

// ----- data members ---------------------------------------------------

/**
* The connection.
*/
private Connection m_con;

/**
* The db table name.
*/
private 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;

// ----- constructors ---------------------------------------------------
/**
* Constructs DBCacheStore for a given database table.
*
* @param sTableName
* the db table name
*/
public DBCacheStore(String sTableName) {
this.m_sTableName = sTableName;
try {
Class.forName(DB_DRIVER);
this.m_con = DriverManager.getConnection(DB_URL, DB_USERNAME,
DB_PASSWORD);
this.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
*/
protected String getTableName() {
return this.m_sTableName;
}

/**
* Obtain the connection being used to connect to the database.
*
* @return the connection used to connect to the database
*/
protected Connection getConnection() {
return this.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 null if
* no value is available for that key
*/
@Override
public Object load(Object oKey) {
Object oValue = null;
String sSQL = "SELECT id, value FROM " + this.getTableName()
+ " WHERE id = ?";
try {
PreparedStatement stmt = this.getConnection()
.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
*/
@Override
public void store(Object oKey, Object oValue) {
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 (this.load(oKey) != null) {
// key exists - update
sSQL = "UPDATE " + this.getTableName()
+ " SET value = ? where id = ?";
} else {
// new key - insert
sSQL = "INSERT INTO " + this.getTableName()
+ " (value, id) VALUES (?,?)";
}
try {
PreparedStatement stmt = this.getConnection()
.prepareStatement(sSQL);
stmt.setString(1, String.valueOf(oValue));
stmt.setString(2, 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
*/
@Override
public void erase(Object oKey) {
String sSQL = "DELETE FROM " + this.getTableName() + " WHERE id=?";
try {
PreparedStatement stmt = this.getConnection()
.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
*/
@SuppressWarnings("rawtypes")
@Override
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
*/
@SuppressWarnings("rawtypes")
@Override
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
*/
@SuppressWarnings("rawtypes")
@Override
public void storeAll(Map mapEntries) {
throw new UnsupportedOperationException();
}
}

P.S.ちょっと書き直しました。
http://twitter.com/#!/wmo6hash/status/23440984759083008