I understand why we would want to have the database be queried if we have, say, a cache Miss/Mrs./Ms. or a cache-hard-down, but I would suggest a more Interface-oriented/Compositional approach, like the below which may end up being more testable and with a consistent interface for cients.
Created
July 2, 2015 23:45
-
-
Save greenantdotcom/d539b130d7efe7f51682 to your computer and use it in GitHub Desktop.
Java example for caching and composition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Assume we had a series of adapters which had this simple interface | |
public interface CacheReadingInterface{ | |
public boolean writeValueToHash(String hashName, String hashKey, Object value); | |
} | |
// And assume you had a Redis cache that implemented it | |
// Further assume RedisConnectionExcepion and RedisProtocolException… | |
public class MyCacheClass implements CacheReadingInterface{ | |
protected Jredis client; | |
public MyCacheClass(Jredis _client){ | |
client = _client; | |
} | |
protected boolean checkHashNameAndKey(String hashName, String hashKey) throws InvalidArgumentException{ | |
String tmpName = hashName.trim(), | |
tmpKey = hashKey.trim(); | |
if(tmpName != hashName){ | |
throw new InvalidArgumentException("Unexpected whitespace in hash name"); | |
} | |
else if(tmpName.length() == 0){ | |
throw new InvalidArgumentException("Hash name is empty"); | |
} | |
if(tmpKey != hashKey){ | |
throw new InvalidArgumentException("Unexpected whitespace in hash key"); | |
} | |
else if(tmpKey.length() == 0){ | |
throw new InvalidArgumentException("Hash key is empty"); | |
} | |
return true; | |
} | |
public boolean writeValueToHash(String hashName, String hashKey, Object value) throws | |
RedisConnectionExcepion, | |
RedisProtocolException, | |
InvalidArgumentException | |
{ | |
checkHashNameAndKey(hashName,hashKey); | |
String valueAsString = value.toString(); | |
// Assume that calling the client here would throw one of the Redis exceptions | |
return client.hset(hashName, hashKey, value.toString()); | |
} | |
public Object getValueFromHash(String hashName, String hashKey) throws | |
RedisConnectionExcepion, | |
RedisProtocolException, | |
InvalidArgumentException { | |
checkHashNameAndKey(hashName,hashKey); | |
// Assume that calling the client here would throw one of the Redis exceptions | |
return client.hget(hashName, hashKey); | |
} | |
} | |
// And assume you had a DB touching adapter class that did roughly the same thing | |
public class MyDBAccessClass implements CacheReadingInterface{ | |
Connection db; | |
PreparedStmt productToPlatformStmt; | |
public MyDBAccessClass(Connection _db){ | |
db = _db; | |
productToPlatformStmt = db.prepare('SELECT * FROM … WHERE platform_id = ?'); | |
} | |
public Object getValueFromHash(String hashName, String hashKey) throws DBException, InvalidException, blah, blah, blah{ | |
// And excuse my using a switch() - was the quickest way to show it off… | |
switch(hashName){ | |
case 'productToPlatform': | |
return productToPlatformStmt.execute(hashKey).fetchOne(); | |
default: | |
throw new InvalidArgumentException("Unknown lookup type <" + hashName + ">"); | |
} | |
} | |
} | |
// Then we can create a Composite object that also implements the interface | |
// that makes us not care where the data is coming from | |
public class DataAcquirer implements CacheReadingInterface{ | |
protected ArrayList<CacheReadingInterface> datasources; | |
public function DataAcquirer(ArrayList<CacheReadingInterface> _datasources){ | |
datasources = _datasources; | |
} | |
public Object getValueFromHash(String hashName, String hashKey) throws Exception { | |
// Then we can loop through the datasources in order | |
for(CacheReadingInterface source : datasources){ | |
try{ | |
// If they can return, they will… | |
return source.getValueFromHash(hashName,hashKey); | |
} | |
catch(Exception e){ | |
// Log me | |
} | |
} | |
// And if we exhaust all sources, throw a specific exception to note this | |
throw new DataNotFoundException("Exhausted datasources - no value found"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This works, right? | |
import *.*; | |
Jredis redis = …; | |
Connection connection = …; | |
ArrayList<CacheReadingInterface> lookupClient = new ArrayList( | |
new MyCacheClass(jredis), | |
new MyDBAccessClass(connection) | |
); | |
try{ | |
Object value = lookupClient.getValueFromHash('productToPlatform','w'); | |
} | |
catch(Exception e){ | |
// Ohs nos… | |
} |
On line 13 of runtime.java, don't you need to get an item from the ArrayList first before calling getValueFromHash?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Slick! I like this!