Skip to content

Instantly share code, notes, and snippets.

@nosavvy33
Last active July 21, 2020 20:45
Show Gist options
  • Save nosavvy33/4ede4413e54b82f0945d780221aba5a3 to your computer and use it in GitHub Desktop.
Save nosavvy33/4ede4413e54b82f0945d780221aba5a3 to your computer and use it in GitHub Desktop.

Preps

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();

Theory considerations

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).

CRUD

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

COMPLEX OBJECTS (a.k.a. Objects inside object)

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

IMPLEMENTATION

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment