JDO在Google App Engine上加载不一致

时间:2011-07-29 22:51:58

标签: google-app-engine junit jdo google-data-api

我一直在尽可能地关注app引擎JDO文档,但是我在加载Board对象包含的持久集合时遇到了困难和不一致的问题。在手动将“最终一致性”指定为不存在之后,即使在本地开发Web服务器中也会出现不一致。

有时当我使用我创建的加载助手方法加载我的对象/集合时,它没有加载任何问题。其他时候会返回一个空集合(请注意我 am “使用getter方法”触摸“该集合以确保数据不仅仅是一个延迟加载到代理对象中)。

最初我认为问题只是由于高复制存储引擎的“最终一致性”缺点而相关,但是在LocalServiceTestHelper中制定了我自己的0%最终一致性策略之后,我很确定情况并非如此。 / p>

我已经创建了一个JUnit测试来举例说明这个问题。基本上,我尝试在testInsertUser函数中创建并保存一个虚拟的User和Board对象。我将新创建的PlayedTile对象的ArrayList附加到此板,然后执行DataMaster.saveUser帮助器方法,该方法使用Google App Engine的持久性管理器将用户(以及Board和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;
}

奇怪的是,这个代码SOMETIMES按预期工作:它在testReadUser()中从数据存储区加载后,打印出它保存的完全相同的一组tile。有时它只是加载一个空集合,虽然特别令人讨厌的是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:

完全随意。

那里有人可以对此有所启发吗?这让我完全疯了。 :)

编辑:另一个奇怪的线索/事实是,如果我通过在for循环中包含testSaveUser和testReadUser方法调用来完成整个测试迭代,则每个加载操作都可以正确执行,或者没有他们这样做。这是本地Google App Engine测试环境中的错误吗?

1 个答案:

答案 0 :(得分:1)

只是为了检查:您是否将数据类标记为可拆卸?即。

@PersistenceCapable(detachable = "true")
public class Board { /* fun stuff here */ }

此外,查看数据存储区查看器以查看是否真的有效可能会有所帮助:http://localhost:8888/_ah/admin/