Here is a an update version of the DAO from the previous post. I added update methods (very useful)…
Next I will be working on some relationship type logic. Meaning, having a table that holds relationships and specifying you want those relationships (a list of ids) that will query that, then query the actual keys table and return the objects you want…
Sorry about not posting on Github. Maybe one day I’ll get it up there.
package wookets.dynamo
import groovy.util.logging.Log4j
import org.springframework.stereotype.Repository
import wookets.util.DateUtil
import wookets.util.ResourceTypeUtil
import com.amazonaws.auth.AWSCredentials
import com.amazonaws.auth.PropertiesCredentials
import com.amazonaws.services.dynamodb.AmazonDynamoDBClient
import com.amazonaws.services.dynamodb.model.AttributeAction
import com.amazonaws.services.dynamodb.model.AttributeValue
import com.amazonaws.services.dynamodb.model.AttributeValueUpdate
import com.amazonaws.services.dynamodb.model.DeleteItemRequest
import com.amazonaws.services.dynamodb.model.DeleteItemResult
import com.amazonaws.services.dynamodb.model.GetItemRequest
import com.amazonaws.services.dynamodb.model.GetItemResult
import com.amazonaws.services.dynamodb.model.Key
import com.amazonaws.services.dynamodb.model.PutItemRequest
import com.amazonaws.services.dynamodb.model.PutItemResult
import com.amazonaws.services.dynamodb.model.ReturnValue
import com.amazonaws.services.dynamodb.model.UpdateItemRequest
import com.amazonaws.services.dynamodb.model.UpdateItemResult
@Log4j
@Repository
class DynamoDao {
AmazonDynamoDBClient client
DynamoDao() {
InputStream credentialsAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(“AwsCredentials.properties”);
AWSCredentials credentials = new PropertiesCredentials(credentialsAsStream);
client = new AmazonDynamoDBClient(credentials);
}
// read
def load(Object resourceType, String resourceId) {
log.debug “Loading ${resourceType}::${resourceId}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
GetItemRequest request = new GetItemRequest(tableName, new Key(toDynamo(resourceId)))
GetItemResult result = client.getItem(request)
def obj = resourceType instanceof Class ? resourceType.newInstance() : [_type: tableName] // return typed or dynamic
result.item.each { String prop, AttributeValue val ->
obj[prop] = fromDynamo(val)
}
return obj
}
// save
def save(Object resource) {
log.debug “Saving ${resource}”
def item = [:] // item to save
if(resource.class == null) { // dynamic object
resource.each { String prop, Object val ->
if(prop == “_type”) return; // strip out ‘_type’
if(val == null) return; // dont waste space with null values
item[prop] = toDynamo(val)
}
} else { // typed object
resource.properties.each { String prop, Object val ->
if(prop == “class”) return; // strip out java added properties
if(prop == “metaClass”) return; // strip out groovy added properties
if(val == null) return; // we don’t support storing nulls
item[prop] = toDynamo(val)
}
}
String tableName = ResourceTypeUtil.resolveType(resource) // table name
PutItemRequest request = new PutItemRequest(tableName, item)
PutItemResult result = client.putItem(request)
}
// delete
def delete(Object resourceType, String resourceId) {
log.debug “Deleting ${resourceType}::${resourceId}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
DeleteItemRequest request = new DeleteItemRequest(tableName, new Key(toDynamo(resourceId)))
DeleteItemResult result = client.deleteItem(request)
}
// update
def addToProperty(Object resourceType, String resourceId, String propertyName, Object propertyValue) {
log.debug “Adding ${resourceType} - ${resourceId} - ${propertyName} - ${propertyValue}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
Key key = new Key(toDynamo(resourceId))
def updateItems = new HashMap()
updateItems.put(propertyName, new AttributeValueUpdate(toDynamo(propertyValue), AttributeAction.ADD))
UpdateItemRequest request = new UpdateItemRequest(tableName, key, updateItems);
UpdateItemResult result = client.updateItem(request);
}
def replaceProperty(Object resourceType, String resourceId, String propertyName, Object propertyValue) {
log.debug “Replacing ${resourceType} - ${resourceId} - ${propertyName} - ${propertyValue}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
Key key = new Key(toDynamo(resourceId))
def updateItems = new HashMap()
updateItems.put(propertyName, new AttributeValueUpdate(toDynamo(propertyValue), AttributeAction.PUT))
UpdateItemRequest request = new UpdateItemRequest(tableName, key, updateItems);
UpdateItemResult result = client.updateItem(request);
}
def removeFromProperty(Object resourceType, String resourceId, String propertyName, Object propertyValue) {
log.debug “Removing ${resourceType} - ${resourceId} - ${propertyName} - ${propertyValue}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
Key key = new Key(toDynamo(resourceId))
def updateItems = new HashMap()
updateItems.put(propertyName, new AttributeValueUpdate(toDynamo(propertyValue), AttributeAction.DELETE))
UpdateItemRequest request = new UpdateItemRequest(tableName, key, updateItems)
UpdateItemResult result = client.updateItem(request)
}
def removeProperty(Object resourceType, String resourceId, String propertyName) {
log.debug “Removing ${resourceType} - ${resourceId} - ${propertyName}”
String tableName = ResourceTypeUtil.resolveType(resourceType)
String hashKey = resourceId
def updateItems = new HashMap()
Key key = new Key().withHashKeyElement(new AttributeValue().withS(hashKey))
updateItems.put(propertyName, new AttributeValueUpdate().withAction(AttributeAction.DELETE));
def updateItemRequest = new UpdateItemRequest().withTableName(tableName).withKey(key).withAttributeUpdates(updateItems);
UpdateItemResult result = client.updateItem(updateItemRequest);
}
// private helper methods
private AttributeValue toDynamo(Object value) {
if(value instanceof Number) { // number support
return new AttributeValue().withN(value)
} else if(value instanceof String) { // string support
return new AttributeValue().withS(value)
} else if(value instanceof List) { // list support
if(value[0] instanceof Number) { // number list support
return new AttributeValue().withNS(value)
} else if(value[0] instanceof String) { // string list support
return new AttributeValue().withSS(value)
}
} else if(value instanceof Boolean) { // boolean support
return new AttributeValue().withS(value ? “bool:true” : “bool:false”)
} else if(value instanceof Date) { // date support
return new AttributeValue().withS(“date:” + DateUtil.dateFormatter.format(value))
} else {
throw new RuntimeException(“Unsupported data type for value ${value}”)
}
}
private Object fromDynamo(AttributeValue value) {
if(value.getS() != null) {
String val = value.getS()
if(val == “bool:true”) { // boolean support
return true
} else if(val == “bool:false”) {
return false
} else if(val?.startsWith(“date:“)) { // date support
return DateUtil.dateFormatter.parse(val.substring(5))
} else { // string support
return val
}
} else if(value.getN() != null) { // number support
return value.getN()
} else if(value.getSS() != null) { // set of strings support
return value.getSS()
} else if(value.getNS() != null) { // set of numbers support
return value.getNS()
}
}
}