Cannot Save Multiple Objects To DynamoDB? - java

I'm attempting to add multiple entries to my DynamoDB database through a service, but when the code is executed, Only one entry is saved. This is what I have done so far:
public int onStartCommand(Intent intent, int flags, int startId) {
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(), // Context
"us-east-1:851c121e-326d-4c9e-be47-fec40eb7b693", // Identity Pool ID
Regions.US_EAST_1 // Region
);
AmazonDynamoDB client = new AmazonDynamoDBClient(credentialsProvider);
Toast.makeText(this, "Adding To Database...", Toast.LENGTH_LONG).show();
CognitoCachingCredentialsProvider cognitoProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(), // Context
"us-east-1:851c121e-326d-4c9e-be47-fec40eb7b693", // Identity Pool ID
Regions.US_EAST_1 // Region
);
AmazonDynamoDBClient ddbClient = new AmazonDynamoDBClient(cognitoProvider);
DynamoDBMapper mapper = new DynamoDBMapper(ddbClient);
String streamName = intent.getStringExtra("streamName");
String userCreated = intent.getStringExtra("userCreated");
ArrayList<String> selectedFriend = intent.getStringArrayListExtra("userInvolved");
intent.getStringExtra("comment");
for(int i = 0; i < selectedFriend.size(); i++){
Streams streams = new Streams();
streams.setStreamname(streamName);
streams.setUsercreated(userCreated);
streams.setUserinvolved(selectedFriend.get(i));
streams.setUnread(true);
streams.setDeleted(false);
mapper.save(streams);
}
}
And like i said, only one entry will be saved. I was suggested to use an arraylist, but I'm not sure how to go about doing that. Can anyone help?
All help is appreciated.
EDIT: The Streams.java class:
#DynamoDBTable(tableName = "Streams")
public class Streams{
private String streamname;
private String usercreated;
private String userinvolved;
private boolean unread;
private boolean deleted;
#DynamoDBAttribute(attributeName = "Stream_Name")
public String getStreamname() {
return streamname;
}
public void setStreamname(String streamname) {
this.streamname = streamname;
}
#DynamoDBHashKey(attributeName = "User_Created")
public String getUsercreated() {
return usercreated;
}
public void setUsercreated(String usercreated) {
this.usercreated = usercreated;
}
#DynamoDBAttribute(attributeName = "User_Involved")
public String getUserinvolved() {
return userinvolved;
}
public void setUserinvolved(String userinvolved) {
this.userinvolved = userinvolved;
}
#DynamoDBAttribute(attributeName = "Unread")
public boolean isUnread() {
return unread;
}
public void setUnread(boolean unread) {
this.unread = unread;
}
#DynamoDBAttribute(attributeName = "Deleted")
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}

It seems your DynamoDB hash key is the user created field in the Stream class. This is the DynamoDB primary key which uniquely identifies a single item in the DynamoDB table.
Each time you call mapper.save(streams), you are overwriting the item you wrote in the previous iteration of the loop since it seems you only set the user created field once here:
String userCreated = intent.getStringExtra("userCreated");.
Try using a unique identifier (e.g. stream name as the hash key). If multiple items have the same hash key you can also use a schema with a range key to create a (hash key, range key) primary key to uniquely identify an item in the DynamoDB table.

DynamodbMapper has a batch save method that allows you to save multiple objects in a batch. Check out the DynamoDBMapper.batchSave.

Related

All my firebase field get automatically an underscore on front

My PoIs class:
public class PoIs {
private Integer location_id;
private String location_name;
private String location_address;
public PoIs() {}
public PoIs(Integer location_id, String location_name, String location_address) {
this();
this.location_id = location_id;
this.category_id = category_id;
this.location_name = location_name;
this.location_address = location_address;
}
public Integer get_location_id() {
return location_id;
}
public void set_location_id(Integer location_id) {
this.location_id = location_id;
}
public String get_location_name() {
return location_name;
}
public void set_location_name(String location_name) {
this.location_name = location_name;
}
public String get_location_address() {
return location_address;
}
public void set_location_address(String location_address) {
this.location_address = location_address;
}
I populate PoIs with informatision from a sqlite database:
final PoIs p = new PoIs(Integer.parseInt(row.get(0).toString()), row.get(1).toString(), row.get(2).toString());
and at a moment intend to save them on a firabase database:
FIREBASE_REFERENCE.child("PoI_"+ p.get_location_id()).setValue(p)
.addOnCompleteListener(t -> {
final boolean isSuccessful = t.isSuccessful();
final String msg = !isSuccessful
? getResources().getString(R.string.fb_error)
: getResources().getString(R.string.fb_success);
});
All work perfect except that my firebase fields start with an underscore. Instead location_id, location_name, location_address I have _location_id, _location_name, _location_address. I can't understand why this happening. Any ideea how to resolve this issue?
Firebase uses JavaBean naming conventions when mapping from properties in your code to properties in the database. In that convention a method like get_location_name is the getter for a property called _location_name.
If you want the property in the database to be location_name, that'd be a getter getLocation_name. Alternatively, you can use a #PropertyName("location_name")) annotation on all accessors (so the getter/setter function and/or the public field) to indicate the explicit property name you want in the database.

Very slow save operation with DynamoDB on local machine

I am trying to use DynamoDB on my local pc.
Before I was using MongoDB and the performance of the DynamoDB compared to it is very poor.
The save operation to a table takes a very long time, about 13 seconds for 100 records.
The records are pretty small, example below.
Here is my full example and code which I use to run it:
public class dynamoTry {
private AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-east-2"))
.build();
private DynamoDB dynamoDB = new DynamoDB(client);
private DynamoDBMapper mapper = new DynamoDBMapper(client);
public static void main(String[] args) {
dynamoTry dt = new dynamoTry ();
dt .deleteTable();
dt .buildGrid();
dt .demoFill();
dt .scanTable();
}
public void buildGrid() {
System.out.println("Attempting to create table; please wait...");
String tableName = "Grid";
List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("name").withAttributeType(ScalarAttributeType.S));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("country").withAttributeType(ScalarAttributeType.S));
List<KeySchemaElement> keySchema = new ArrayList<KeySchemaElement>();
keySchema.add(new KeySchemaElement().withAttributeName("name").withKeyType(KeyType.HASH));
keySchema.add(new KeySchemaElement().withAttributeName("country").withKeyType(KeyType.RANGE));
CreateTableRequest request = new CreateTableRequest().withTableName(tableName).withKeySchema(keySchema)
.withAttributeDefinitions(attributeDefinitions).withProvisionedThroughput(
new ProvisionedThroughput().withReadCapacityUnits(500L).withWriteCapacityUnits(500L));
Table table = dynamoDB.createTable(request);
try {
table.waitForActive();
System.out.println("Success.");
} catch (InterruptedException e) {e.printStackTrace();}
}
public void demoFill() {
final List<GridPoint> gpl = new ArrayList<GridPoint>();
int count = 0;
while (count < 100) {
final String point = "point" + count;
gpl.add(makeGP(point, count, "continent", "country", new HashSet<Double>(Arrays.asList(22.23435, 37.89746))));
count++;
}
long startTime = System.nanoTime();
addBatch(gpl);
long endTime = System.nanoTime();
long duration = (endTime - startTime)/1000000;
System.out.println(duration + " [ms]");
}
public void addBatch(List<GridPoint> gpl) {
mapper.batchSave(gpl);
}
public GridPoint makeGP(String name, int sqNum, String continent, String country, HashSet<Double> cords) {
GridPoint item = new GridPoint();
item.setName(name);
item.setSqNum(sqNum);
item.setContinent(continent);
item.setCountry(country);
item.setCoordinates(cords);
return item;
}
public void scanTable() {
Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":val", new AttributeValue().withN("0"));
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression().withFilterExpression("sqNum >= :val").withExpressionAttributeValues(eav);
List<GridPoint> scanResult = mapper.scan(GridPoint.class, scanExpression);
for (GridPoint gp : scanResult) {
System.out.println(gp);
}
}
public void deleteTable() {
Table table = dynamoDB.getTable("Grid");
try {
System.out.println("Attempting to delete table 'Grid', please wait...");
table.delete();
table.waitForDelete();
System.out.print("Success.");
}
catch (Exception e) {
System.err.println("Unable to delete table: ");
System.err.println(e.getMessage());
}
}
}
Here is the code for the GridPoint class:
#DynamoDBTable(tableName = "Grid")
public class GridPoint {
private String name;
private int sqNum;
private String continent;
private String country;
private HashSet<Double> coordinates; // [longitude, latitude]
// Partition key
#DynamoDBHashKey(attributeName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#DynamoDBAttribute(attributeName = "sqNum")
public int getSqNum() {
return sqNum;
}
public void setSqNum(int sqNum) {
this.sqNum = sqNum;
}
#DynamoDBAttribute(attributeName = "continent")
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
#DynamoDBAttribute(attributeName = "country")
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
#DynamoDBAttribute(attributeName = "coordinates")
public HashSet<Double> getCoordinates() {
return coordinates;
}
public void setCoordinates(HashSet<Double> coordinates) {
this.coordinates = coordinates;
}
#Override
public String toString() {
return "GP {name = " + name + ", sqNum = " + sqNum + ", continent = " + continent + ", country = " + country
+ ", coordinates = " + coordinates.toString() + "}";
}
}
Why is it so slow? is there any way of speeding the writing process?
In MongoDB the same operations would take less than a second.
When I was running it about 3000 points it took several minutes to finish, seems not reasonable.
Is it possible to make the batch save process parallel? would it speed things up?
I also tried to set the ProvisionedThroughput parameter to a higher value but that did not help.
I am lost, any help would be appreciated, thank you.
It's is slow because it is not DynamoDB. There is no Local DynamoDB!
DynamoDB is a managed service provided by AWS and it is really fast (milliseconds for the first bytes), highly scalable and durable. It is a really good product with a lot of performance for a small amount of money. But it is a managed service. It only works on AWS environment. There is no way to you or anyone else get a copy and install DynamoDB in Azure, GCP or even in your local environment.
What are you using is a facade, probably developed by AWS Team to help developers test their applications. There are other DynamoDB facades, not developed by AWS Team but everyone of then just respect a protocol that accepts all api calls from the original product. As a facade, his objective is just provide a endpoint that can receive your calls and respond like the original product. If you make a call that the original DynamoDB would respond with an Ok the facade will respond with an Ok. If you make a call that the original DynamoDB would respond with a failure the facade will send you a failure.
There is no compromisse with performance or even data durability. If you need a durable database, with good performance, you must go with MongoDB. DynamoDB was created to be used on AWS environment only.
Again: There is no such thing like DynamoDB local.
DynamoDB has predefined limits. It is possible that you are running into these limits. Consider increasing WriteCapacityUnits for the table to increase performance. You may also want to increase ReadCapacityUnits for the scan.

get all values from one class to anothed class method

I have an class IntegrationWithDB in which i have to method getConnection()and selectFromDB().
In the selectFromDb() i have a result set , i want to get the result
set vales in another class method
Actually it did but it only shows the last value of dataBase table.
Note i have made getter and setter method in IntegrationWithDB class and use in selectFromDB() method.
public void selectFromDB() {
try {
if (this.conn == null) {
this.getConnection();
}
if (this.stmt == null) {
this.stmt = this.conn.createStatement();
}
int success = 0;
this.query = "select * from contacts order by node_id";
this.rs = this.stmt.executeQuery(query);
// something is wrong in the while loop
while (rs.next()) {
setId(rs.getInt("node_id")); // i made getter and setter for id, name, parent and for level
setNam(rs.getString("node_name"));
setParnt(rs.getString("node_parent"));
setLvl(rs.getInt("node_parent"));
}
if (success == 0) {
this.conn.rollback();
} else {
this.conn.commit();
}
} catch (Exception ex) {
ex.printStackTrace();
}
and in another class test i have method displayList() in this method i write the following code
public class test {
IntegrationWithDbClass qaz = new IntegrationWithDbClass();
public void displayList ( ) {
qaz.getConnection();
qaz.selectFromDB();
for(int i = 0; i< 5; i++){
System.out.println(" "+qaz.getId());
System.out.println(" "+qaz.getNam());
}
}
when i initilize the displayList() method in the main method , it shows the following result
5
red
how can i get all the five values?
First of all you have to create what is commonly referred to as an Entity class. This is the class that represents a single row in your database. This should ideally be separate from the code that interacts with the database connection.
So first step, create a class named Contact, and in it put the 4 fields you have, id, name, parent and level, with the respective getter methods. If you do not expect these to change by your program make them immutable, it is the good practice to ensure consistency. So something like:
public class Contact {
private final int id;
private final String name;
private final String parent;
private final String level;
public Contact(String id, String name, String parent, String level) {
this.id = id;
this.name = name;
this.parent = parent;
this.level = level;
}
public int getId() {
return id;
}
//... put the rest of the getter methods
}
Then in your IntegrationWithDB class (I would rename this to something more meaningful like ContactRepository) you can change that method you have to:
public List<Contact> getContacts() {
// ... your database connection and query code here
this.rs = this.stmt.executeQuery(query);
List<Contact> contacts = new LinkedList<Contact>();
while (rs.next()) {
int id = rs.getInt("node_id");
String name = rs.getString("node_name");
String parent = rs.getString("node_parent");
String level = setLvl(rs.getInt("level"));
contacts.add(new Contact(id, name, parent, level));
}
//... the rest of your database handling code, don't forget to close the connection
return contacts;
}
Then from displayList() you just have to call getContacts() which gives you a list of Contact objects to iterate through.
I assume that currently you're storing those properties in int/string variables. In every iteration of the loop you're overwriting the values. What you need to do is to store them in some collection like ArrayList and in each iteration add() to this collection.

DynamoDBMappingException: no HASH key value present

I have a class named SampleEntity i.e a POJO which will help me create my dynamoDB table.The hash key and range key have been defined clearly in POJO object but still i get an exception that the hash key is not being defined
#DynamoDBTable(tableName = "sampletable1")
public class SampleEntity {
public static final String HASH_KEY = "f1_hash";
public static final String RANGE_KEY = "f2_range";
#DynamoDBAttribute(attributeName = HASH_KEY)
#DynamoDBHashKey
private Integer feild1;
#DynamoDBAttribute(attributeName = RANGE_KEY)
#DynamoDBRangeKey
private String field2;
#DynamoDBAttribute(attributeName = "f3")
private String feild3;
#DynamoDBAttribute(attributeName = "f4")
private String feild4;
#DynamoDBAttribute(attributeName = "f5")
private String feild5;
public Integer getFeild1() {
return feild1;
}
public void setFeild1(Integer feild1) {
this.feild1 = feild1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getFeild3() {
return feild3;
}
public void setFeild3(String feild3) {
this.feild3 = feild3;
}
public String getFeild4() {
return feild4;
}
public void setFeild4(String feild4) {
this.feild4 = feild4;
}
public String getFeild5() {
return feild5;
}
public void setFeild5(String feild5) {
this.feild5 = feild5;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SampleEntity)) return false;
SampleEntity that = (SampleEntity) o;
if (!getFeild1().equals(that.getFeild1())) return false;
if (!getField2().equals(that.getField2())) return false;
if (!getFeild3().equals(that.getFeild3())) return false;
if (!getFeild4().equals(that.getFeild4())) return false;
return getFeild5().equals(that.getFeild5());
}
#Override
public int hashCode() {
int result = getFeild1().hashCode();
result = 31 * result + getField2().hashCode();
result = 31 * result + getFeild3().hashCode();
result = 31 * result + getFeild4().hashCode();
result = 31 * result + getFeild5().hashCode();
return result;
}
}
This my class and i am issuing a create table request on this class but i got DynamoDBMappingException that is no HASH key value present.
server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", "8005"});
server.start();
dynamoDBClient = new AmazonDynamoDBClient(new BasicAWSCredentials("any", "thing")).withEndpoint("http://localhost:8005");
dbMapper = new DynamoDBMapper(dynamoDBClient);
CreateTableRequest createTableRequest = ddbMapper.generateCreateTableRequest(SampleEntity.class);
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput(5L, 5L));
dynamoDBClient.createTable(createTableRequest);
SampleLoginEntity data= new SampleLoginEntity();
data.setLogin(123);
data.setField2("range");
data.setFeild3("abc");
dbMapper.save(data);
There are two possible issues I can see (one that I ran into recently), but your setup is a little different than mine.
You're using both #DynamoDBAttribute and #DynamoDBHashKey on a single item - that's not necessary, and might be messing it up, though I don't have time to test it right now. You should be able to just do, #DynamoDBHashKey(attributeName=HASH_KEY) and you'll be fine. I think as is, you might be declaring an attribute as "f1_hash", and a hash key named as "field1", both mapping to the same internal value (though I could be wrong).
The issue I was having though is actually a result of this error message being really poorly worded - it will throw this exception when you call dbMapper.save() with an object with the hash key value set to null, though if your setLogin() was supposed to be setField1() that shouldn't be the issue here.
I got the same exeption when tried to get the record by the null hashCode:
myClassMapper.load(MyClass.class, null))
Try to set:
data.setField1("some_hash");

Hibernate One-To-Many Mapping, printing mapped lists

I have used One-to-Many Mapping in my project. I have stored a list of clicks for every user.
But when I retrieve the list by calling getClicks() methodm Hibernate returns list in different format.
Something like this.
"[com.zednx.tech.persistence.Click#29df9a77]"
So I tried Reading Every value from the list and assign to a new List.
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
clicks.add(temp);
}
But when i print the items of new List it stills prints the same way.
I need to build a JSON from the resulting String of this list.
So if the list is returned in format, it wont help me.
I couldn't find anything regarding this except How to pretty print Hibernate query results?
I tried Arrays.ToString(Object o). But it doesn't work.
GSON builder part-
Gson gson = new GsonBuilder()
.registerTypeAdapter(Click.class, new MyTypeAdapter<Click>())
.create();
List<Click> clicks=new ArrayList<Click>();
for(Click c: e.getClicks()){
Click temp = new Click();
temp.setAff_source(c.getAff_source());
temp.setCb_to_award(c.getCb_to_award());
temp.setCb_type(c.getCb_type());
temp.setCom_to_recieve(c.getCom_to_recieve());
temp.setStore_name(c.getStore_name());
temp.setT_date(c.getT_date());
temp.setT_status(c.getT_status());
temp.setT_ticket(c.getT_ticket());
temp.setUid(c.getUid());
System.out.println(c.toString());
clicks.add(temp);
}
String json = gson.toJson(clicks, Click.class);
Click.java
#Entity
#Table(name="click")
public class Click {
#Id
#Column(name="t_ticket")
private String t_ticket;
#Column(name="uid",nullable=false)
private long uid;
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
#ManyToOne
#JoinColumn(name="uid",
insertable=false, updatable=false,
nullable=false)
private Earning earning;
#Column(name="store_name")
private String store_name;
#Column(name="t_status")
private String t_status;
#Column(name="aff_source")
private String aff_source;
#Column(name="com_to_recieve")
private float com_to_recieve;
#Column(name="t_date")
private Date t_date;
#Column(name="cb_to_award")
private float cb_to_award;
#Column(name="cb_type")
private String cb_type;
public String getT_ticket() {
return t_ticket;
}
public void setT_ticket(String t_ticket) {
this.t_ticket = t_ticket;
}
public Earning getEarning() {
return earning;
}
public void setEarning(Earning earning) {
this.earning = earning;
}
public String getStore_name() {
return store_name;
}
public void setStore_name(String store_name) {
this.store_name = store_name;
}
public String getT_status() {
return t_status;
}
public void setT_status(String t_status) {
this.t_status = t_status;
}
public String getAff_source() {
return aff_source;
}
public void setAff_source(String aff_source) {
this.aff_source = aff_source;
}
public float getCom_to_recieve() {
return com_to_recieve;
}
public void setCom_to_recieve(float com_to_recieve) {
this.com_to_recieve = com_to_recieve;
}
public Date getT_date() {
return t_date;
}
public void setT_date(Date t_date) {
this.t_date = t_date;
}
public float getCb_to_award() {
return cb_to_award;
}
public void setCb_to_award(float cb_to_award) {
this.cb_to_award = cb_to_award;
}
public String getCb_type() {
return cb_type;
}
public void setCb_type(String cb_type) {
this.cb_type = cb_type;
}
Any Help is appreciated.
You need to implement a toString method, as your current Click class likely doesn't have one, so it just prints as the name of the class and instance identifier.
Okay, I could solve my problem finally.
I made another POJO without any annotations and Mapped the List items to that POJO class.
I think the problem was with Annotation of mapping on another class which I had in original POJO.
Also getString() method only helps in changing format of identifier. So basically it has nothing to do with JSON building unless you format getString() in form of JSON.
Hope it helps. If anyone wants new temp POJO I made I can post it if requested.
Thanks.

Categories

Resources