我一直在尽可能地关注应用程序引擎 JDO 文档,但是在加载我的 Board 对象包含的持久集合时遇到了奇怪且不一致的问题。在我手动将“最终一致性”指定为不存在后,即使在本地开发 Web 服务器中也会出现不一致。
有时,当我使用我创建的加载辅助方法加载我的对象/集合时,它加载没有问题。其他时候会返回一个空集合(请注意,我正在使用 getter 方法“接触”该集合,以确保数据不仅仅是延迟加载到代理对象中)。
最初我认为这个问题只是由于高复制存储引擎的“最终一致性”缺陷而导致的,但在我自己制定了 LocalServiceTestHelper 中最终一致性为 0% 的策略之后,我相当确定情况并非如此。
我创建了一个 JUnit 测试来说明这个问题。基本上,我尝试在 testInsertUser 函数中创建并保存一个虚拟的 User 和 Board 对象。我将一个新创建的 PlayedTile 对象的 ArrayList 附加到此板,然后执行 DataMaster.saveUser 辅助方法,该方法使用 Google App Engine 的持久性管理器将用户(以及板和 PlayedTile 集合)保存到数据存储区。在下一个方法中,我们尝试加载该用户(及其 Board 和 PlayedTile 集合)并显示那些保存的结果。混乱随之而来。
这是 JUnit 代码:
package com.astar.wordswall.test.data;
import java.util.ArrayList;
import com.astar.wordswall.data.DataMaster;
import com.astar.wordswall.data.jdo.Board;
import com.astar.wordswall.data.jdo.User;
import com.astar.wordswall.data.jdo.PlayedTile;
import com.astar.wordswall.test.appengine.LocalCustomHighRepPolicy;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
// import com.google.gwt.user.client.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class SaveUsersBoardWithTilesTest {
Key userKey;
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
.setAlternateHighRepJobPolicyClass(LocalCustomHighRepPolicy.class));
@Before
public void setUp() {
helper.setUp();
}
@After
public void tearDown() {
helper.tearDown();
}
@Test
public void testInsert1() {
testInsertUser();
testReadUser();
}
/**
* Creation and insertion of a user, board, and linked set of tiles into BigTable.
*/
private void testInsertUser() {
User u = new User("Simon");
Board b = new Board();
ArrayList<PlayedTile> tiles = new ArrayList<PlayedTile>(7);
u.setBoard(b);
b.setPlayedTiles(tiles);
for (int j = 0; j < 7; j++) tiles.add(new PlayedTile('T'));
DataMaster.saveUser(u);
// Retrieve the user's key so that we can read him from the database later
userKey = u.getUserKey();
// Display all of our saved tiles:
System.out.println("Saved tiles:");
// Note that "getTileString()" just iterates through each Played tile printing the letter
System.out.println("\t" + u.getBoard().getTileString());
}
/**
* A typical read of a user object from the Datastore.
*/
private void testReadUser() {
User u = DataMaster.getUserWithBoard(userKey);
// Display all of our saved tiles:
System.out.println("Loaded tiles:");
System.out.println("\t" + u.getBoard().getTileString());
}
}
这是实际执行 JDO 加载的相关 DataMaster.getUserWithBoard 静态函数:
/**
* Loads a uniquely specified User and their associated board from
* the Datastore. It also loads the board's complete list of PlayedTiles.
* @param userKey the unique key assigned to this user
*/
public static User getUserWithBoard(Key userKey){
User u = null;
PersistenceManager pm = PMF.get().getPersistenceManager();
try{
u = pm.getObjectById(User.class, userKey);
// In order for the board and tile collection to load, we must "touch" it while PM is active
if (u.getBoard().getPlayedTiles().size() != 0) u.getBoard().getPlayedTiles().get(0);
if (u.getBoard().getPlayedWords().size() != 0) u.getBoard().getPlayedWords().get(0);
} finally{
pm.close();
}
return u;
}
奇怪的是,这段代码有时会按预期工作:它会打印出与 testReadUser() 中的数据存储区加载后保存的完全相同的图块集。有时它只是加载一个空集合,尽管特别奇怪的是 u.getBoard().getPlayedWords().get(0) 调用不会引发空指针异常。
输出在之间振荡
正确的:
Saved tiles:
T T T T T T T
Loaded tiles:
T T T T T T T
并且不正确:
Saved tiles:
T T T T T T T
Loaded tiles:
完全随意。
那里的任何人都可以对此有所了解吗?这让我彻底疯了。:)
编辑:另一个奇怪的线索/事实是,如果我通过将 testSaveUser 和 testReadUser 方法调用都包含在 for 循环中来使整个测试迭代,则每个加载操作都可以正确执行,或者它们都不会正确执行。这是本地 Google App Engine 测试环境中的错误吗?