Recommended way to persist Map<String, Long> field?

2,501 views
Skip to first unread message

charles spirakis

unread,
Jan 13, 2012, 1:27:01 PM1/13/12
to objectify-appengine
All --

I'm interested in using objectify to store a Map<String, Long> field
in a java object. If I try to declare it as:

@Embedded
Map<String, Long> stuff = new HashMap<String, Long>();

I get an error:
[java] Caused by: java.lang.IllegalStateException: java.lang.Long
has no constructor with args []
...
[java] Caused by: java.lang.NoSuchMethodException:
java.lang.Long.<init>()

If I don't use the @Embedded tag, then appengine complains because Map
is not a native type.

[java] java.lang.IllegalArgumentException: countersLong:
java.util.HashMap is not a supported property type.

What is the recommended way to persist Map<String, Long> fields in
objectify?

Thank you.

-- charles

Jeff Schnitzer

unread,
Jan 26, 2012, 11:38:12 PM1/26/12
to objectify...@googlegroups.com, bez...@gmail.com
Hi there, sorry about the late reply (Google Groups "lost" your
message for a while).

Presuming you want the map values stored as a series of properties
with a Long value (for example, stuff.put("abc", 123) would produce a
property "stuff.abc" with value 123L), I suggest you use Objectify4
(grab from trunk) and leave off the @Embedded annotation. It will
work as you expect.

Jeff

Richard Watson

unread,
Jan 27, 2012, 1:55:52 AM1/27/12
to objectify...@googlegroups.com, bez...@gmail.com
Same issue as I had - look a couple threads below.  Charles, you'll need to rewrite some of the code using Objectify to move to 4, but at least it's a once-off and you're future-proofing it.

Issues I experienced:
1) No DAOBase, or I couldn't find it.  Added an ofy() method to my own data access parent class so I could move code without too much rewriting.
2) Change ofy().put(entity) to ofy().save().entity(entity).now();
3) Change ofy().get(key) to ofy().load().key(key).get();
4) Change queries to e.g. ofy().load().type(Clazz.class).filter("filter", value).list();

...etc.  Notice the save() and load() methods, and chain from there.  Also, leaving off the now() on save() means it'll happen async, but I didn't have time to test that out properly.  Maybe somebody has improvements they can suggest.

Regards,
Richard

Matthew Jaggard

unread,
Jan 27, 2012, 9:17:43 AM1/27/12
to objectify...@googlegroups.com
If I recall correctly, Objectify didn't used to do this. I currently persist a class which just contains a Map<String, Set<String>> manually - am I right in thinking this could be stored automatically now? My current class puts every item in the map into a property on the entity when converting in one direction and puts all properties into the map when converting back. Will this work (for new entities, I don't expect old entities to be read) for what I need? If I call my map "m" I'll just be spending 2 bytes more per property yeah?

Maybe I can finally lose all my low-level API code.

Thanks,
Mat.

Jeff Schnitzer

unread,
Jan 27, 2012, 2:10:03 PM1/27/12
to objectify...@googlegroups.com
On Fri, Jan 27, 2012 at 9:17 AM, Matthew Jaggard <mat...@jaggard.org.uk> wrote:
> If I recall correctly, Objectify didn't used to do this. I currently persist
> a class which just contains a Map<String, Set<String>> manually - am I right
> in thinking this could be stored automatically now? My current class puts
> every item in the map into a property on the entity when converting in one
> direction and puts all properties into the map when converting back. Will
> this work (for new entities, I don't expect old entities to be read) for
> what I need? If I call my map "m" I'll just be spending 2 bytes more per
> property yeah?
>
> Maybe I can finally lose all my low-level API code.

Assuming you're talking Objectify4... yes, that will work. When Ofy4
sees a Map<String, Anything> it works exactly as if the map is an
embedded class with expando-type fields. Take for example these two
entity classes:

@Entity public class Thing {
@Embed public static class Inner {
Long foo;
Long bar;
}

Inner content;
}

@Entity public class Thing {
Map<String, Long> content;
}

If you put "foo" and "bar" in the Map of the 2nd entity, they read and
write the exact same datastore entity. In fact, they are
plug-replaceable... you can read and write the same data int the
datastore.

This works whether the Map value is a Long, String, Set<String>,
@Embed class, Key<?>, whole @Entity (in which case it stores a Key
mapping), etc. As long as the Map key is a String, objectify treats
it like an embedded class.

How did you store your Map<String, Set<String>>? Does the entity
property look like:

foo.key1 = ["abc", "def"]
foo.key2 = ["ghi", "jkl"]

If so you can switch to a POJO with a Map<String, Set<String>> field
named 'foo'.

Jeff

charles spirakis

unread,
Jan 29, 2012, 3:14:28 PM1/29/12
to objectify-appengine
Good to know.

I don't think we're ready to move to objectify 4 yet, but useful to
know what we can do now to make that transition easier in the future.

-- charles


On Jan 27, 11:10 am, Jeff Schnitzer <j...@infohazard.org> wrote:
Reply all
Reply to author
Forward
0 new messages