如何使用ancestor()正确存储和查询Objectify实体?

时间:2015-02-26 03:33:34

标签: java google-app-engine objectify

存储和查询涉及标有Objectify @Parent注释的字段的Objectify实体的正确方法是什么?请使用ancestor()查询提供示例。

1 个答案:

答案 0 :(得分:4)

有几个要求:

  1. 索引 - 确保查询的字段已编入索引

  2. 祖先 - 请务必使用ancestor(parent)

  3. 提交 - 确保已提交保存

  4. 附件 - 确保将孩子附加到父母并保存

  5. 验证ids是否匹配,而不是Java对象实例 - 当您将其读回时,它将是一个不同的Java对象。但标有Objectify的@Id的字段应该是相同的

  6. 使用.now()保存并使用.now()回读 - 请参阅Why .now()? (Objectify)Objectify error "You cannot create a Key for an object with a null @Id" in JUnit

  7. 请勿尝试使用标记为@Parent的字段作为您要过滤的字段;重复该字段

  8. datastore-indexes.xml - 如果您要查询实体并对多个字段进行过滤,则Objectify的@Index注释是不够的。您还必须在[datastore-indexes.xml](https://cloud.google.com/appengine/docs/java/config/indexconfig)中输入一个条目。感谢Patrice在评论中提到这一点。

  9. 这是我正在编写的一些代码的草图,我将其用于单元测试。

    <强> RepositoryTest

    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.Matchers.equalTo;
    import com.googlecode.objectify.ObjectifyService;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    public class RepositoryTest {
        // Tests under worst case of replication https://stackoverflow.com/questions/27727338/which-is-better-setdefaulthighrepjobpolicyunappliedjobpercentage100-vs-custo
        private final LocalServiceTestHelper helper =
                new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
                    .setDefaultHighRepJobPolicyUnappliedJobPercentage(100)); 
    
        private Closeable closeable;
        private Repository repository;
    
        @Before
        public void setup() {
            helper.setUp();
            repository = new Repository();
            ObjectifyService.register(MyCategory.class);
            ObjectifyService.register(MyItem.class);
            closeable = ObjectifyService.begin(); // https://stackoverflow.com/questions/27726961/how-to-resolve-you-have-not-started-an-objectify-context-in-junit
        }
    
        @After
        public void tearDown() {
            closeable.close();
            helper.tearDown();
        }
    
        @Test
        public void testLookupMyItemShouldSucceed() {
            MyCategory myCategory = repository.createMyCategory();
    
            int zero = 0;
            int one = 1;
            int two = 2;
    
            addMyItem(myCategory, zero, "a");
            MyItem expectedMyItem = addMyItem(myCategory, one, "b");
            addMyItem(myCategory, two, "c");
    
            MyItem actualMyItem = repository.lookupMyItem(myCategory, one);
    
            assertThat(actualMyItem, Matchers.notNullValue());
            assertThat(actualMyItem.id, equalTo(expectedMyItem.id));
        }
    
        private MyItem addMyItem(MyCategory myCategory, long index, String label) {
            MyItem myItem = repository.createMyItem();
            myItem.setParent(myCategory);
            myItem.setGroup(myCategory);
            myItem.index = index;
            myItem.label = label;
            repository.updateMyItem(myItem);
        }
    }
    

    <强>存储库

    import static com.googlecode.objectify.ObjectifyService.begin;
    import static com.googlecode.objectify.ObjectifyService.ofy;
    import com.googlecode.objectify.util.Closeable;
    
    public class Repository {
        public Topic createMyCategory() {
            Topic entity = topicProvider.get();
            updateTopic(entity);
            return entity;
        }   
    
        public MyItem lookupMyItem(MyCategory myCategory, long i) {
            return ofy().load().type(MyItem.class).ancestor(myCategory).filter(MyItem.MyCategoryField, myCategory).filter(MyItem.IndexField, i).first().now();
        }
    }
    

    <强> MyItem

    import com.googlecode.objectify.Ref;
    import com.googlecode.objectify.annotation.Entity;
    import com.googlecode.objectify.annotation.Id;
    import com.googlecode.objectify.annotation.Index;
    import com.googlecode.objectify.annotation.Parent;
    
    @Entity
    public class MyItem {
        @Id public Long id;
        @Parent private Ref<MyCategory> parent;
    
        @Index private Ref<MyCategory> myCategory; public static final String MyCategoryField = "myCategory";   
        @Index public Long index; public static final String IndexField = "index"; 
    
        public String label;
        public long weight;
    
        public MyCategory getGroup() {
            return group.get();
        }
    
        public void setGroup(MyCategory group) {
            this.group = Ref.create(group);
        }
    
        public MyCategory getParent() {
            return parent.get();
        }
    
        public void setParent(MyCategory group) {
            this.parent = Ref.create(group);
        }
    }
    

    <强> MyCategory

    import com.googlecode.objectify.annotation.Entity;
    import com.googlecode.objectify.annotation.Id;
    
    @Entity
    public class MyCategory {
        @Id public Long id;
    }
    

    <强>数据存储-indexes.xml

    <?xml version="1.0" encoding="utf-8"?>
    <datastore-indexes autoGenerate="true">
        <datastore-index kind="MyItem" ancestor="true">
            <property name="myCategory" direction="asc" />
            <property name="index" direction="asc" />
        </datastore-index>
    </datastore-indexes>
    

    上面的代码中可能存在语法错误或拼写错误,因为为了清晰起见,我从原始代码修改了此错误。单元测试确实通过并且它一致地传递(即,由于eventual consistency,它有时不会失败。)