At first glance the first problem is that generic fields like
Map<String, ROLE> can't work automatically. Objectify performs static
analysis of the class to determine how to store things, and with
erasure, ROLE is Object.
That said, there should be ways of making this work, not least by
specifying your own @Translate translators. Objectify can't guess
what translator to use, so you have to be explicit.
I'm surprised that you get back an empty value... if something was
saved, you should get something back (although not necessarily what
you want to get back).
Drop the @Embed from the field. @Embed should be specified on the
class that gets embedded. We may remove the ability to put @Embed on
fields before release.
Hope this at least gives you a *little* guidance to start...
Jeff
On Tue, Jun 19, 2012 at 5:20 PM, Wayne Rasmuss <
wayne....@gmail.com> wrote:
> I've been having a heck of time with an embedded hash map. I'm using
> Objectify 4.0.a3. I included the jar downloaded from a link in a forum post.
>
> The class with the problem is pasted below. The problem is the roles member
> (private HashMap<String, ROLE> roles = new HashMap<String, ROLE>();) is not
> loaded. Currently I'm linitializing the value with an empty map so that's
> what I get, I've tried not initializing it, and I get roles = null. Finally,
> it seems like the map works in some contexts and not others, so I've
> included the class that references the problems class as well.
>
> I've spent some time with the debugger in the objectify code (acquired from
> GIT) I wasn't able to fetch 4.0.a3. So, I've been debugging 4.0.a1. So far,
> my breakpoints and stepping through code seems solid so I think I've been
> looking at the right code. I've attached some screen shots of my debugger at
> break points and the logs (ofy level = finest) while the object in questions
> is saved and loaded. From the logs it looks like the map is saved correctly,
> but it is loaded empty. The key for the entity is missing the member
> is List("d9c294dc-d800-4c5b-9b13-a488efe725ed")/LinkCollection(1) You should
> be able to go straight to it in the log using that.
>
> I'm going to keep looking the problem, but any help anyone can give would be
> greatly appreciated. I'll be happy to provide more information if required.
>
> Thanks!
> Wayne
>
> *************************************** THE CLASS WITH THE EMPTY MEMBER MAP
> *****************************************************
> /*
> * Copyright (c) 2012. What's Next Software, LLC all rights reserved
> */
>
> package whatsnext.exodus.coreservice.bidilinks;
>
> import com.googlecode.objectify.Key;
> import com.googlecode.objectify.annotation.Cache;
> import com.googlecode.objectify.annotation.Embed;
> import com.googlecode.objectify.annotation.Entity;
> import
com.googlecode.objectify.annotation.Id;
> import com.googlecode.objectify.annotation.Load;
> import com.googlecode.objectify.annotation.Parent;
> import com.googlecode.objectify.annotation.Unindex;
> import whatsnext.exodus.coreservice.SimpleStore;
>
> import java.util.Collection;
> import java.util.HashMap;
> import java.util.HashSet;
> import java.util.LinkedList;
> import java.util.Map;
> import java.util.Set;
>
> /**
> * This class contains a bunch of links. Each link may optionally have a
> role. Therefor this class must be careful
> * to distinguish between items in the set that do not exist and items that
> exist, but do not have roles
> * @param <T> the type of object that the relationship is to
> * @param <ROLE> the type of object that represents the role. An enum is
> recommended and may be required in the future
> */
> @Entity
>
> public class LinkCollection<T, P, ROLE> {
>
> @Id
> long id = 1;
>
> @Parent
> private Key<P> parent;
>
> @Embed
> @Unindex
> private HashMap<String, ROLE> roles = new HashMap<String, ROLE>(); //<-
> THIS IS WHAT TURNS UP EMPTY
>
> public LinkCollection(Key<P> parent) {
> this.parent = parent;
> }
>
> private LinkCollection() {
> }
>
> boolean resolve(Map<T, ROLE> result, SimpleStore<T> store) {
> Map<Key<T>, T> resolvedObjects = store.get(getKeys(), null);
> Set<String> removed = new HashSet<String>();
> for(Key<T> current : getKeys()) {
> T currentReconciled = resolvedObjects.get(current);
> if (currentReconciled != null) {
>
> result.put(currentReconciled,roles.get(current.getString()));
> } else {
> removed.add(current.getString());
> }
> }
> for (String toRemove : removed) {
> roles.remove(toRemove);
> }
> return removed.size() > 0;
> }
>
> public ROLE get(Key<T> key) {
> return roles.get(key.getString());
> }
>
> public void setParent(Key<P> parent) {
> this.parent = parent;
> }
>
> @Override
> public boolean equals(Object obj) {
> boolean returnValue = false;
> if (this == obj) {
> returnValue = true;
> } else if (obj instanceof LinkCollection) {
> LinkCollection asMyType = (LinkCollection)obj;
> returnValue = roles.equals(asMyType.roles);
> }
> return returnValue;
> }
>
> public boolean remove(Key<T> key) {
> boolean returnValue = roles.containsKey(key.getString());
> roles.remove(key.getString());
> return returnValue;
> }
>
> public boolean put(Key<T> target, ROLE role) {
> boolean returnValue = false;
> if (role == null) {
> returnValue = !roles.containsKey(target.getString());
> }
> ROLE oldValue = roles.put(target.getString(), role);
> if (role != null) {
> returnValue = !role.equals(oldValue);
> }
> return returnValue;
> }
>
> public Collection<Key<T>> getKeys() {
> Collection<Key<T>> returnValue = new LinkedList<Key<T>>();
> for(String asString : roles.keySet()) {
> returnValue.add(Key.<T>valueOf(asString));
> }
> return returnValue;
> }
>
> public boolean hasRoleFor(Key<T> parent) {
> return roles.containsKey(parent.getString());
> }
> }
>
> *************************************** CLASS REFERENCING THE PROBLEM CLASS
> ABOVE *****************************************************
>
> import com.googlecode.objectify.annotation.Entity;
> import
com.googlecode.objectify.annotation.Id;
> import com.googlecode.objectify.annotation.Load;
> import com.googlecode.objectify.annotation.Parent;
> import com.googlecode.objectify.annotation.Unindex;
> import com.googlecode.objectify.impl.ref.StdRef;
> import com.googlecode.objectify.util.ResultNow;
> import whatsnext.exodus.coreservice.SimpleStore;
>
> import java.util.Map;
>
> @Cache
> @Entity
> public class LeaderToFollowerLinks<LEADER, FOLLOWER, ROLE> {
>
> @SuppressWarnings({"FieldCanBeLocal"})
> @Id
> private long id = 1;
>
> @Parent
> private Key<LEADER> parent;
>
> @Unindex
> private long version = 0;
>
> @Unindex
> private Ref<LinkCollection<FOLLOWER, LEADER, ROLE>> roles; //<- HERE'S
> THE REFERENCE TO THE PROBLEM CLASS. I'VE TRIED EMBED, AND JUST A PLAIN
> REFERENCE
>
> public LeaderToFollowerLinks(Key<LEADER> parent) {
> this.parent = parent;
> Key rolesKey = Key.create(parent, LinkCollection.class, 1l);
> roles = new StdRef<LinkCollection<FOLLOWER, LEADER,
> ROLE>>(rolesKey);
> }
>
> protected LeaderToFollowerLinks() {
>
> }
>
> public void putRole(Key<FOLLOWER> target, ROLE role) {
> if(roles.getValue() == null) {
> roles.set(new ResultNow<LinkCollection<FOLLOWER, LEADER,
> ROLE>>(new LinkCollection<FOLLOWER, LEADER, ROLE>(parent)));
> }
> roles.getValue().put(target, role);
> }
>
> public void removeRole(Key<FOLLOWER> follower) {
> if(roles.getValue() != null) {
> roles.getValue().remove(follower);
> }
> }
>
> public void resolveLinks(Map<FOLLOWER, ROLE> destination,
> SimpleStore<FOLLOWER> followerStore) {
> if(roles.getValue()!= null) {
> roles.getValue().resolve(destination, followerStore);
> }
> }
>
> public long incrementVersion() {
> return ++version;
> }
>
> public long getVersion() {
> return version;
> }
>
> public ROLE getRoleFor(Key<FOLLOWER> follower) {
> ROLE returnValue = null;
> if(roles.getValue() != null) {
> returnValue = roles.getValue().get(follower);
> }
> return returnValue;
> }
>
> public Key<LEADER> getParent() {
> return parent;
> }
>
> @Override
> public boolean equals(Object obj) {
> boolean returnValue = false;
> if (this == obj) {
> returnValue = true;
> } else if (obj instanceof LeaderToFollowerLinks) {
> LeaderToFollowerLinks asMyType = (LeaderToFollowerLinks)obj;
> returnValue = parent.equals(asMyType.getParent()) && id ==
> asMyType.id && version == asMyType.version && roles.equals(asMyType.roles);
> }
> return returnValue;
> }
>
> public boolean hasRoleFor(Key<FOLLOWER> follower) {
> boolean returnValue = false;
> if(roles.getValue() != null) {
> returnValue = roles.getValue().hasRoleFor(follower);
> }
> return returnValue;
> }
>
> public LinkCollection<FOLLOWER, LEADER, ROLE> getRoles() {
> return roles.getValue();
> }
>
> }
>
>