Once No-SQL (Dynamo) is integrated in your Mobile Hub project, update your awsconfiguration.json and implemented data models, which are mapped objects, proceed to make a CRUD.
As DynamoDB calls are synchronous and they don't belong to the main UI Thread of the Activity, then all operations directed to your Dynamo Tables gotta be inside an asynchronous method like Runnable
Runnable runnable = new Runnable() {
public void run() {
//Whatever you do with DynamoDB here
}
};
Thread mythread = new Thread(runnable);
mythread.start();
Given Mapped Object Analysis In the given mapped object from the mobile hub, it comes with some annotations out-of-the-box worth-knowing-what-they-for:
@DynamoDBTable(tableName = "your-table-name") : it describes the name of your table and to where all the operations within the below object will be redirected to.
@DynamoDBHashKey(attributeName = "field") : this is the, so to speak, primary key of your table, read the next point to know more about it.
@DynamoDBAttribute(attributeName = "field") : this indicates that this field has its representation in the table in the cloud, it maps your attributes and points which attrs will be store and which are to be kept local only. ATTENTION: As DynamoDB is a NoSQL DB, you can add fields to your document just by specifying a DynamoDBAttribute above the getMethod of your new-come attribute, which doesn't necessarily had to be defined at the creation of your table
@DynamoDBRangeKey(attributeName = "field") : this indicates your range key inside your object, read the next point to know more about it.
Partition vs Range Key A partition key is similar to a primary key (in the code is referred as HashKey), whereas the rangeKey would be the ORDER BY column by one sorts out a result. Keep in mind these two concepts!!!
NOTICE : You CAN'T have the partition key and sort key as the same attribute, you CAN ignore sort key (yet don't know the consequences in performance for this take)
Query vs Scan QUERY | SCAN -returns only one document | -can return MORE THAN one document -needs a condition statement | -needs a filter expression -needs json to gson conversion | -already handles mapping objects
SUGGESTION Always prefer Scan over Query as query needs of HashKey (Partition key) and RangeKey (SortKey).
CREATE
public void createNews() {
//initialize an empty mapped object
final MyObjectDO newsItem = new MyObjectDO();
// set their attributes
newsItem.setId("6");
newsItem.setAge("11");
newsItem.setSex("aaaaaa");
newsItem.set_room("Piso 9");
//run it
new Thread(new Runnable() {
@Override
public void run() {
Log.e("CREATED :",newsItem.toString());
dynamoDBMapper.save(newsItem);
// Item saved
}
}).start();
}
READ - QUERY
public void readNews() {
new Thread(new Runnable() {
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public void run() {
MyObjectDO news = new MyObjectDO();
news.setId("5"); // set the partition (primary) key
//full reference of Condition AWS Object
//https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/model/AttributeValue.html#withN-java.lang.String-
Condition rangeKeyCondition = new Condition()
.withComparisonOperator(ComparisonOperator.EQ) //EQ stands for Equals (beware data types)
.withAttributeValueList(new AttributeValue().withS("44")); //withS stands for With String
DynamoDBQueryExpression queryExpression = new DynamoDBQueryExpression()
.withHashKeyValues(news) //already set hashkey
.withRangeKeyCondition("age", rangeKeyCondition) //rangekey are ONLY defined at the creation of the table
.withConsistentRead(false);
PaginatedList<MyObjectDO> result = dynamoDBMapper.query(MyObjectDO.class, queryExpression);
Gson gson = new Gson();
StringBuilder stringBuilder = new StringBuilder();
// Loop through query results
for (int i = 0; i < result.size(); i++) {
String jsonFormOfItem = gson.toJson(result.get(i));
stringBuilder.append(jsonFormOfItem + "\n\n");
}
// Toast.makeText(getApplicationContext(),"Query result : "+stringBuilder.toString(),Toast.LENGTH_SHORT).show();
Log.e("Query result: ", stringBuilder.toString());
if (result.isEmpty()) {
// There were no items matching your query.
Log.e("EMPTY :",": RESULT");
}
// Item read
// Log.d("News Item:", news.toString());
}
}).start();
}
READ - SCAN
public void scanItems(){
new Thread(new Runnable() {
@Override
public void run() {
//Biding filter values
Log.i("Process :",": Scanning Items");
Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":val1", new AttributeValue().withS("44"));
//setting filter expression
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
.withFilterExpression("age = :val1").withExpressionAttributeValues(eav);
//mapping results
List<PatientsDO> scanResult = dynamoDBMapper.scan(PatientsDO.class, scanExpression);
for (PatientsDO book : scanResult) {
// System.out.println(book);
Log.e("Scanned",book.toString());
}
}
}).start();
}
UPDATE
public void updateNews() {
final PatientsDO booksItem = new PatientsDO();
booksItem.setId("5"); //this is the partition (primary) key, so this one will be updated
//if no index are defined, the first result from the search will be updated
booksItem.setAge("44");
booksItem.setSex("GGGGGG");
// booksItem.setTitle("Escape from Slavery");
new Thread(new Runnable() {
@Override
public void run() {
// Using .save(bookItem) with no Title value makes that attribute value equal null
// The .Savebehavior shown here leaves the existing value as is
dynamoDBMapper.save(booksItem, new DynamoDBMapperConfig(DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES));
Log.e("UPDATED :",booksItem.toString());
// Item updated
// readNews();
}
}).start();
}
UPDATE (WORKING June, 22, 2018) GET YOUR DYNAMOCLIENT
AWSMobileClient.getInstance().initialize(this).execute();
AWSCredentialsProvider credentialsProvider = AWSMobileClient.getInstance().getCredentialsProvider();
AWSConfiguration configuration = AWSMobileClient.getInstance().getConfiguration();
AmazonDynamoDBClient dynamoDBClient = new AmazonDynamoDBClient(credentialsProvider);
SET VALUES FOR PRIMARY(/PARTITION/HASH KEY) AND YOUR RANGE(/SORT KEY, IF EXISTS). IMPORTANT: Can't update either primary or range key, already tried it, do not waste ur time.
HashMap<String, AttributeValue> key = new HashMap<String, AttributeValue>();
// PRIMARY KEY
key.put("codigoQR", new AttributeValue().withS(id));
// RANGE KEY
key.put("sala", new AttributeValue().withS(room));
// SET NEW-COME VALUES TO UPDATE OLD ONES
Map<String, AttributeValue> expressionAttributeValues = new HashMap<String, AttributeValue>();
expressionAttributeValues.put(":val2",new AttributeValue().withN(String.valueOf(age)));
expressionAttributeValues.put(":val3", new AttributeValue().withS(sex));
// INDICATE TO RETURN THE JUST UPDATED ITEM
ReturnValue returnValues = ReturnValue.ALL_NEW;
//SET UPDATE REQUEST
UpdateItemRequest updateItemRequest = new UpdateItemRequest()
.withTableName("cognitoandroid-mobilehub-368169605-testing")
.withKey(key)
//JUST AS ANY UPDATE, USE A SET STATEMENT
.withUpdateExpression("set edad = :val2, sexo = :val3")
//THE VALUES ABOVE DEFINED TO REPLACE THE OLD ONES
.withExpressionAttributeValues(expressionAttributeValues)
//INDICATE TO RETURN JUST UPDATED ITEM
.withReturnValues(returnValues)
;
//SEND UPDATE REQUEST
UpdateItemResult result = MainMenuActivity.statClient.updateItem(updateItemRequest);
Gson gson = new Gson();
String x = gson.toJson(result);
Log.e("RESULT :",x);
For further information about the methods, use this working documentation (notice it is low level, if there's a wayaround with high level methods, i ignore them): https://github.com/aws-samples/aws-dynamodb-examples/blob/master/src/main/java/com/amazonaws/codesamples/lowlevel/LowLevelItemCRUDExample.java
Sometimes custom objects gotta be included Dynamo already mapped objects. So, for this cause, either Marshall or JsonMarshall implementation are necessary
Marshall or JsonMarshall??
AWS Dynamo Docs specify that for keeping a low storage usage, prefer Marshalling your object instead of JsonMarshalling If you marshall it then it is stored as a String, otherwise, it is store as a json
So there is laying a POJO with its constructor and getters and setters:
public class MyWhat(){
private String name;
private String last;
public MyWhat(String name, String last) {
this.name = name;
this.last = last;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now, to marshall, so to speak it is based on the employees in the airport so they're no more than just transporter or communicators,create a MyWhatMarshaller
Opt 1: String Object Marshalling
//See that it implements DynamoDBMarshaller of type the object you're to marshall
public class MyWhatMarshaller implements DynamoDBMarshaller<MyWhat> {
@Override
public String marshall(Test getterReturnResult) {
// in this method, you manage how to put your object to be created and stored, but as a String
return getterReturnResult.getLast()+"/"+getterReturnResult.getName();
}
@Override
public Test unmarshall(Class<Test> clazz, String obj) {
//Here you reconstruct your object using the String obj as the product of the String marshall(Test getterReturnResult)
String [] vals = obj.split("/");
Test test = new Test();
test.setName(vals[0]);
test.setLast(vals[1]);
return test;
}
}
Now, inside your mapped object include the object
@DynamoTable(tableName = "my_tablein_Dynamo")
public class MyObjectDO{
private MyWhat myWhat;
//ATTENTION: the marshallerclass references to the Marshaller one created, NOT the POJO one, BEWARE!
@DynamoDBMarshalling(marshallerClass = MyWhatMarshaller.class)
public getMyWhat(){ return this.myWhat; }
public setMyWhat(MyWhat myWhat){ this.myWhat = myWhat; }
private String aMethod;
@DynamoDBAttribute(attributeName = "aMethod")
public getAMethod(){ return this.aMethod; }
public setAMethod(String aMethod){ this.aMethod = aMethod; }
...
/* In the space in between, included is all the rest of Hashkeys, RangeKey and the remaining attributes of your object
...
public MyObjectDO (MyWhat myWhat, String... remainingAttrs){
this.myWhat = myWhat;
}
}
Once done all this stuff, you can invoke this as any other object inside another object
onCreate(){
...
MyObjectDO kk = new MyObjectDO();
MyWhat mm = new MyWhat("it"," works");
kk.setMyWhat(mm);
... less of the code
}
Opt 2: JSON Object Marshalling
In case you want to store the object as a JSON, then it is simpler than marshalling it. Given your POJO, just create another class and EXTEND from JsonMarshaller
public class MyWhatMarshallerJson extends JsonMarshaller<MyWhat>{
}
And that's it in the implementation, only do not forget to change this
public class MyObjectDO{
...cool code here
private MyWhat myWhat;
//ATTENTION: the marshallerclass references to the Marshaller one created, NOT the POJO one, BEWARE!
// instead of this -> @DynamoDBMarshalling(marshallerClass = MyWhatMarshaller.class), notice MyWhatMarshallerJSON.class
@DynamoDBMarshalling(marshallerClass = MyWhatMarshallerJson.class)
public getMyWhat(){ return this.myWhat; }
...blablabla code
}
Thanks for reading it!