JDO Загрузка несоответствий в Google App Engine - PullRequest
2 голосов
/ 30 июля 2011

Я внимательно следил за документацией JDO движка приложения, но у меня возникли странные и непоследовательные проблемы с загрузкой постоянной коллекции, содержащейся в моем объекте Board.Несоответствие возникает даже в локальном веб-сервере разработки после того, как я вручную указываю «возможную согласованность» как несуществующую.

Иногда, когда я загружаю свой объект / коллекцию с использованием созданных мной вспомогательных методов загрузки, это не вызывает проблем.В других случаях возвращается пустая коллекция (обратите внимание, что я "касаюсь" коллекции с помощью метода get, чтобы гарантировать, что данные загружаются не просто в объект прокси).

Первоначально я думал, что проблема просто связана с недостатками механизма "возможной согласованности" механизма хранения с высокой репликацией, но после создания собственной политики с возможной согласованностью 0% в LocalServiceTestHelper я вполне уверен, что это неcase.

Я создал тест JUnit, который иллюстрирует эту проблему.По сути, я пытаюсь создать и сохранить фиктивный объект User и Board в функции testInsertUser.Я присоединяю недавно созданный ArrayList объектов PlayedTile к этой доске, а затем выполняю вспомогательный метод 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());
    }
}

А вот соответствующая статическая функция DataMaster.getUserWithBoard, которая фактически выполняет загрузку JDO:

/**
 * 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 ().ИНОГДА он просто загружает пустую коллекцию, хотя особенно странно то, что вызов 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:

совершенно беспорядочно.

Может кто-нибудьтам пролить свет на это?Это сводит меня с ума.:)

EDIT : Еще одна странная подсказка / факт: если я сделаю весь тест повторяющимся, включив вызовы методов testSaveUser и testReadUser в цикл for, либо КАЖДЫЕ операции загрузки выполняются правильноили никто из них не делает.Это ошибка в локальной тестовой среде Google App Engine?

1 Ответ

1 голос
/ 31 июля 2011

Просто чтобы проверить: пометили ли вы свои классы данных как съемные?Т.е.

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

Кроме того, может быть полезно взглянуть на просмотрщик хранилища данных, чтобы увидеть, действительно ли он работает: http://localhost:8888/_ah/admin/

...