如果父母不同,同一种类的两个实体可以具有相同的ID吗?

时间:2016-01-20 20:14:44

标签: java google-app-engine google-cloud-endpoints google-cloud-datastore objectify

我知道数据存储区会自动为根实体生成唯一ID。但是那些拥有不同父母的同类实体呢?

数据存储区是否会为具有不同父级(同类)的同一种类的实体生成唯一ID?例如User->Post。可以想象两个不同的用户每个都有一个具有相同ID的帖子吗?

1 个答案:

答案 0 :(得分:2)

我为你写了一个JUnit测试。它使用lombok,但你也可以写出getter和setter。

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.*;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
import com.googlecode.objectify.util.Closeable;
import junit.framework.Assert;
import lombok.Getter;
import lombok.Setter;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.List;

public class IdAllocationTest {
    @Entity
    public static class ChildEntity {
        @Parent
        @Getter
        @Setter
        private Ref<ParentEntity> parent;
        @Id
        @Getter
        @Setter
        private Long id;
    }

    @Entity
    public static class ParentEntity {
        @Id
        @Getter
        @Setter
        private Long id;
    }

    public static class OfyService {
        static {
            try {
                ObjectifyService.register(ChildEntity.class);
                ObjectifyService.register(ParentEntity.class);
            } catch (Exception e) {
                System.out.println("Could not initialized objectify service." + e.toString());
            }
        }

        public static Objectify ofy() {
            return ObjectifyService.ofy();
        }

        public static ObjectifyFactory factory() {
            return ObjectifyService.factory();
        }
    }

    private static LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
    private static Closeable objectifyBegin;

    @BeforeClass
    public static void beforeClass(){
        helper.setUp();
        objectifyBegin = ObjectifyService.begin();
    }

    @AfterClass
    public static void afterClass(){
        objectifyBegin.close();
        helper.tearDown();
    }

    @Test
    public void testIdAllocation() {
        Ref<ParentEntity> parent1 = Ref.create(Key.create(ParentEntity.class, 1L));
        Ref<ParentEntity> parent2 = Ref.create(Key.create(ParentEntity.class, 2L));

        ChildEntity childEntity1 = new ChildEntity();
        childEntity1.setParent(parent1);
        childEntity1.setId(1L);

        ChildEntity childEntity2 = new ChildEntity();
        childEntity2.setParent(parent2);
        childEntity2.setId(1L);

        OfyService.ofy().save().entities(childEntity1, childEntity2).now();

        List<Key<ChildEntity>> keys = OfyService.ofy().load().type(ChildEntity.class).keys().list();
        // If overwriting occurred it would be only a single entity
        Assert.assertEquals(keys.size(), 2);
        for (Key<ChildEntity> child : keys) {

            System.out.println("Key( " +
                    "Key('" + child.getParent().getKind() + "'," + child.getParent().getId() + "), " +
                    "'" + child.getKind() + "', " + child.getId() + ")");
        }

        while(true) {
            KeyRange<ChildEntity> keyRangeParent1 = OfyService.factory().allocateIds(parent1, ChildEntity.class, 100);
            KeyRange<ChildEntity> keyRangeParent2 = OfyService.factory().allocateIds(parent2, ChildEntity.class, 100);

            for (Key<ChildEntity> keyParent1 : keyRangeParent1) {
                for (Key<ChildEntity> keyParent2 : keyRangeParent2) {
                    System.out.println(keyParent1.getId() + ", " + keyParent2.getId());
                    Assert.assertTrue(keyParent1.getId() != keyParent2.getId());
                }
            }
        }
    }
}

在devserver上,此单元测试的输出就像这样开始

Key( Key('ParentEntity',1), 'ChildEntity', 1)
Key( Key('ParentEntity',2), 'ChildEntity', 1)
1, 101
1, 102
1, 103
1, 104
1, 105

这证明了两件事:

  1. 可能具有不同祖先的相同ID
  2. devserver上的行为是id不会冲突(它们似乎使用相同的计数器)。基本上这证明我们不能通过查看devserver证明一件事,但代码可以(理论上)在一个实时系统上运行。
  3. 警告:请不要部署此代码。那里有一个潜在的无限循环,实际击中的可能性非常小。人们必须大幅增加分配ID的数量,并保持一个父母的分配ID以进行比较。即便如此,在测试所有allocateIds之前很久就会遇到DeadlineExceeded和OutOfMemory异常。

    要点: 除非谷歌的某些人能够启发我们如何分配id分配,否则我们找不到多少。快速查看Datastore implementation表明分配是对数据存储区的请求,因此没有可以分析的代码可以深入挖掘。

    我想我们只需要相信documentation是正确的,当它说

      

    避免此类冲突的唯一方法是让您的应用程序使用方法DatastoreService.allocateIds()或AsyncDatastoreService.allocateIds()获取ID块。   数据存储区的自动ID生成器将跟踪ID   已经分配了这些方法,将避免重复使用它们   另一个实体,因此您可以安全地使用此类ID而不会发生冲突。

    关于id分配方法。