I'm using the GAE (Google App Engine) datastore to store persons.
Each person has a name, an unique token, a list of messages they received (Strings) and a list of tests they completed (Strings). These are all supported types.
However, I also want to store a HashMap containing String keys and String values with information on the tests they have completed.
An example Map i'm trying to save (note that this is example data, not actual data)
[
<'test_easy_1', 'completed in 3 minutes'>
<'test_easy_2', 'completed in 5 minutes'>
<'test_hard_1', 'completed in 15 minutes'>
<'test_hard_2', 'completed in 3 minutes.. Did you cheat?'>
]
I'm trying to save the person like this
@Override
public boolean savePerson(Person p) {
if (p.getEntity() == null) {
return false;
}
p.getEntity().setProperty("name", p.getName());
p.getEntity().setProperty("token", p.getToken());
p.getEntity().setProperty("messages", p.getMessages());
p.getEntity().setProperty("completedTests", p.getCompletedTests());
p.getEntity().setProperty("testInformation", p.getTestInformation()); // does not work
DatastoreServiceFactory.getDatastoreService().put(p.getEntity());
return true;
}
Now for my question:
How do I save HashMaps as object properties to the datastore?
I hope to perform this in a way where I don't have to create an entire class and new Datastore index just for the HashMap. However, if this is the only way to go, I'd like to be informed how to do so. Googling this has not resulted in a clear and obvious way to deal with these kind of situations.
HashMap in Java works on hashing principles. It is a data structure that allows us to store object and retrieve it in constant time O(1) provided we know the key. In hashing, hash functions are used to link keys and values in HashMap.
In the ArrayList chapter, you learned that Arrays store items as an ordered collection, and you have to access them with an index number ( int type). A HashMap however, store items in "key/value" pairs, and you can access them by an index of another type (e.g. a String ).
The HashMap will most likely need more memory, even if you only store a few elements. By the way, the memory footprint should not be a concern, as you will only need the data structure as long as you need it for counting. Then it will be garbage collected, anyway.
Albeit other answers are probably correct, I was mainly looking for a way to achieve this without using external libraries.
I have done some research on beforementioned EmbeddedEntity to find that this is indeed what I was looking for.
I'm writing and accepting this answer as it gives an exact solution to my problem, and not just guidelines towards solving it. I hope to guide people in the future who stumble upon similar problems by doing it this way.
Saving the person now looks like this:
@Override
public boolean savePerson(Person p) {
if (p.getEntity() == null) {
return false;
}
p.getEntity().setProperty("name", p.getName());
p.getEntity().setProperty("token", p.getToken());
p.getEntity().setProperty("messages", p.getMessages());
p.getEntity().setProperty("completedTests", p.getCompletedTests());
EmbeddedEntity ee = new EmbeddedEntity();
Map<String, String> testInformation = p.getTestInformation();
for (String key : testInformation.keySet()) { // TODO: maybe there is a more efficient way of solving this
ee.setProperty(key, testInformation.get(key));
}
p.getEntity().setProperty("testInformation", ee);
DatastoreServiceFactory.getDatastoreService().put(p.getEntity());
return true;
}
Loading the person now looks like this:
Person p = new Person(id);
p.setName((String) e.getProperty("name"));
p.setToken((String) e.getProperty("token"));
p.setMessages((List<String>) e.getProperty("messages"));
p.setCompletedTests((List<String>) e.getProperty("completedTests"));
Map<String, String> ti = new HashMap<>();
EmbeddedEntity ee = (EmbeddedEntity) e.getProperty("testInformation");
if (ee != null) {
for (String key : ee.getProperties().keySet()) {
ti.put(key, (String) ee.getProperty(key));
}
p.setTestInformation(ti);
}
p.setEntity(e);
You can save it as an embedded entity.
I'd recommend using Objectify which simplifies storage and reduces boilerplate: https://code.google.com/p/objectify-appengine/wiki/Entities#Maps
You can create indexes by just annotating @Index on the Map field.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With