Bassdrop Cumberwubwubwub Bassdrop Cumberwubwubwub - 23 days ago 7
Java Question

How to save a HashMap (as object property) to the Datastore

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.

Answer

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