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

How properly store and query an Objectify Entity with ancestor()?

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

有几个要求:

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

  2. Ancestor - 确保使用 ancestor(parent)

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

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

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

  6. 使用 .now() 保存并使用 .now() 回读 - 请参阅 and 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 在评论中提到这一点。

这是我正在编写的一些代码的草图,我在单元测试中使用它。

存储库测试

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 
    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(); // 
    }

    @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();
    }
}

我的物品

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

我的类别

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 而失败)。