我知道数据存储区会自动为根实体生成唯一ID。但是那些拥有不同父母的同类实体呢?
数据存储区是否会为具有不同父级(同类)的同一种类的实体生成唯一ID?例如User->Post
。可以想象两个不同的用户每个都有一个具有相同ID的帖子吗?
答案 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
这证明了两件事:
警告:请不要部署此代码。那里有一个潜在的无限循环,实际击中的可能性非常小。人们必须大幅增加分配ID的数量,并保持一个父母的分配ID以进行比较。即便如此,在测试所有allocateIds之前很久就会遇到DeadlineExceeded和OutOfMemory异常。
要点: 除非谷歌的某些人能够启发我们如何分配id分配,否则我们找不到多少。快速查看Datastore implementation表明分配是对数据存储区的请求,因此没有可以分析的代码可以深入挖掘。
我想我们只需要相信documentation是正确的,当它说
时避免此类冲突的唯一方法是让您的应用程序使用方法DatastoreService.allocateIds()或AsyncDatastoreService.allocateIds()获取ID块。 数据存储区的自动ID生成器将跟踪ID 已经分配了这些方法,将避免重复使用它们 另一个实体,因此您可以安全地使用此类ID而不会发生冲突。
关于id分配方法。