I cannot get my JUnit test to work when I query a class that extends my base Entity. It works fine when I query the Entity class itself, just not anything that extends it. I have the @Entity annotation on my base Entity, and the @EntitySubclass annotation on my User (which extends Entity)I am using Objectify 4.0b1Here is my code:Entity.java:package tv.ux1.entity;import com.google.appengine.labs.repackaged.org.json.JSONObject;import com.googlecode.objectify.Key;import com.googlecode.objectify.annotation.Ignore;import com.googlecode.objectify.annotation.Index;/*** Entity contains methods and members that are common to all entity classes, such as {@link #toJSON} and {@link #toString}.* <p/>* Copyright 2012: ux1.tv** @author Joe Ernst* Date: 7/9/12*/@com.googlecode.objectify.annotation.Entitypublic class Entity {Key key;@IdLong id;@IndexString name;/*** @return A JSON representation of this entity.*/public String toJSON() {JSONObject json = new JSONObject(this);json.remove("class"); // we don't want the class name in the json objectreturn json.toString();}/*** Calls {@link #toJSON()}.** @return A JSON String representation of this Message.*/public String toString() {return this.toJSON();}public Key getKey() {return key;}public void setKey(Key key) {this.key = key;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}User.java:package tv.ux1.entity;import com.googlecode.objectify.annotation.Cache;import com.googlecode.objectify.annotation.EntitySubclass;import com.googlecode.objectify.annotation.Index;import javax.mail.internet.AddressException;import javax.mail.internet.InternetAddress;import javax.persistence.Id;/*** A POJO to contain everything relevant to a User.** Created by : Joe Ernst* Date: 07/03/12*/@Cache@EntitySubclasspublic class User extends Entity {/*** The user's real first name.*/String firstName;/*** The user's real last name.*/String lastName;/*** A unique email address that identifies this user.*/@IndexString emailAddress;public User() {super();}public User(String emailAddress) {setEmailAddress(emailAddress);}// Keep the generated methods together for ease of re-generation if necessarypublic String getEmailAddress() {return emailAddress;}public void setEmailAddress(String emailAddress) {this.emailAddress = emailAddress;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}}DataAccessObject.java:package tv.ux1.dao;import com.googlecode.objectify.Key;import com.googlecode.objectify.Ref;import com.googlecode.objectify.Result;import com.googlecode.objectify.cmd.Query;import tv.ux1.entity.Entity;import java.util.logging.Logger;import static tv.ux1.util.OfyService.ofy;/*** Copyright 2012: ux1.tv** @author Joe Ernst* Date: 11/25/12*/public class DataAccessObject {protected final Logger logger = Logger.getLogger(DataAccessObject.class.getName());public Ref<? extends Entity> get(Key<? extends Entity> entityKey) {return ofy().load().key(entityKey);}public Ref<? extends Entity> get(Class<? extends Entity> entityClass, Long entityId) {return ofy().load().type(entityClass).id(entityId);}public Query<? extends Entity> filter(Class<? extends Entity> entityClass, String field, String value ) {return ofy().load().type(entityClass).filter(field, value);}public Result<Key<Entity>> save(Entity entity) {return ofy().save().entity(entity);}public Result<Void> delete(Key entityKey) {return ofy().delete().key(entityKey);}}and finally,TestDataAccessObject.java:package tv.ux1.test.dao;import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;import com.google.appengine.tools.development.testing.LocalServiceTestHelper;import com.googlecode.objectify.Key;import com.googlecode.objectify.Result;import org.junit.After;import org.junit.Assert;import org.junit.Before;import org.junit.Test;import tv.ux1.dao.DataAccessObject;import tv.ux1.entity.Entity;import tv.ux1.entity.User;import static org.junit.Assert.*;/*** Copyright 2012: ux1.tv** @author Joe Ernst* Date: 12/6/12*/public class TestDataAccessObject {private final LocalServiceTestHelper helper =new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());DataAccessObject dao = null;/*** Creates a new DataAccessObject instance.*/@Beforepublic void setUp() {helper.setUp();dao = new DataAccessObject();}@Afterpublic void tearDown() {helper.tearDown();}@Testpublic void createEntity() {try {Entity entity = new Entity();Key<Entity> key = dao.save(entity).now();assertNotNull(key);} catch (Exception e) {e.printStackTrace();fail(e.getMessage());}}@Testpublic void queryEntity() {try {Entity entity = new Entity();entity.setName("Foo");Key<Entity> key = dao.save(entity).now();assertNotNull(key);// This works; I can query the Entity by nameEntity foundEntity = dao.filter(Entity.class, "name", "Foo").first().get();assertEquals(entity.getName(), foundEntity.getName());} catch (Exception e) {e.printStackTrace();fail(e.getMessage());}}@Testpublic void queryUser() {try {User user = new User();user.setEmailAddress("f...@bar.com");Key key = dao.save(user).now();assertNotNull(key);// This works; I can get the User by keyUser userByKey = (User)dao.get(key).get();assertEquals(user.getEmailAddress(), userByKey.getEmailAddress());// This works; I can get the User by IdUser userById = (User) dao.get(User.class, key.getId()).get();assertEquals(user.getEmailAddress(), userById.getEmailAddress());// This doesn't work; foundUser is nullUser foundUser = (User)dao.filter(User.class, "emailAddress", "f...@bar.com").first().get();assertEquals(user.getEmailAddress(), foundUser.getEmailAddress());} catch (Exception e) {e.printStackTrace();fail(e.getMessage());}}}Am I doing this correctly?
Thanks,Joe
user.setEmailAddress("foo@bar.com");
Key key = dao.save(user).now();assertNotNull(key);// This works; I can get the User by keyUser userByKey = (User)dao.get(key).get();assertEquals(user.getEmailAddress(), userByKey.getEmailAddress());// This works; I can get the User by IdUser userById = (User) dao.get(User.class, key.getId()).get();assertEquals(user.getEmailAddress(), userById.getEmailAddress());// This doesn't work; foundUser is nullUser foundUser = (User)dao.filter(User.class, "emailAddress", "f...@bar.com").first().get();assertEquals(user.getEmailAddress(), foundUser.getEmailAddress());} catch (Exception e) {e.printStackTrace();fail(e.getMessage());}}}
Thanks for you patience Jeff!I think I understand why it worked when I added (index=true); it's because that caused the underlying discriminator to be indexed. That sounds like extra overhead to me, and led to the question, did I have my data model correct?I originally had my base Entity class annotated with @Entity, and the User (and every other Entity subtype) annotated with @EntitySubclass. Based on your comments, I moved the @Entity annotation to the User class (and every other class that directly extends Entity). This means my base Entity class has no annotations, even though it is extended by all of my entities. I should only need the @EntitySubclass annotation on classes that further extend User, etc.I hope that was the right answer, because all of my unit tests are passing and I'm ready to move on the the next concept that I'm struggling with! ;-)