지속성을 위해 JDO 및 DataNucleus를 사용하는 App Engine을 실험하고 있습니다. 몇 가지 단방향 관계가 포함 된 간단한 도메인이 있습니다. (1-1) - -> 클랜 JDO, App Engine, Data Nucleus, JUnit에서 일관성없는 지속성 연결 동작
- 문명 : 문제는 그 관계를 중첩 함께 제공 - (1-N) -> 군대 (이 불일치)
- 문명 - (1-N 참조) - DataNucleus Documentation 따르면> 정착
, 지속성별로 도달 의미론에 연계하여 모든 유지해야 문명의 지속. 이러한 객체의 기본 저장 및 검색을 검사하는 JUnit 테스트가 있지만 동작이 일관되지 않습니다. 코드를 변경하지 않으면 테스트를 반복하여 실행하면 비 결정적 결과가 발생합니다. 특히 군대는 단지 약 50 %의 시간 동안 만 지속됩니다. 그것들은 실패하는 유일한 시험입니다.
군대가 지속되지 않는 시나리오를 더 쉽게 이해할 수 있지만, 불규칙한 행동으로 인해 저를 잃어 버렸습니다. 그 밖의 모든 것은 정확하고 일관되게 지속됩니다. 트랜잭션에서 팩토리 메소드를 래핑하려고 시도했지만 양방향 관계를 시도했지만 JUnit에서 50/50 합격/불합격 분할을 변경하지 않았습니다.
App Engine 설명서 (스팸 방지 조치로 인해 포함되지 않은 링크)에 설명 된대로 DataNucleus에 대한 주석 기반 구성을 사용하고 있습니다. 나는 많은 양의 코드가 첨부되어 사과드립니다. 나는 어디서 잘못 될지 모르겠다.
CivilizationCreateTest.java :
package com.moffett.grunzke.server;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.moffett.grunzke.generic.GenericHelperFactory;
import com.moffett.grunzke.server.civilization.Army;
import com.moffett.grunzke.server.civilization.Civilization;
import com.moffett.grunzke.server.civilization.Clan;
import com.moffett.grunzke.server.civilization.Land;
import com.moffett.grunzke.server.civilization.Military;
import com.moffett.grunzke.server.civilization.Settlement;
@SuppressWarnings("unchecked")
public class CivilizationCreationTest
{
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig(),
new LocalUserServiceTestConfig())
.setEnvIsLoggedIn(true)
.setEnvEmail("[email protected]")
.setEnvAuthDomain("google.com");
@Before
public void setUp()
{
helper.setUp();
}
@After
public void tearDown()
{
helper.tearDown();
}
@Test
public void testCivilizationCreation()
{
String clanName = "Test Clan";
String rulerName = "Test Ruler";
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
if (user == null)
{
fail("No user");
}
PersistenceManager pm = PMF.get().getPersistenceManager();
CivilizationFactory.newInstance(user, clanName, rulerName);
// We check to make sure that 1, and only 1 Civilization has been made.
Query q1 = pm.newQuery("SELECT FROM " + Civilization.class.getName());
List<Civilization> allCivilizations = (List<Civilization>) q1.execute();
assertTrue(allCivilizations.size() == 1);
// Now we move on to checking the other aspects.
Civilization persistentCiv = allCivilizations.get(0);
Clan persistentClan = persistentCiv.getClan();
Land persistentLand = persistentCiv.getLand();
Military persistentMilitary = persistentCiv.getMilitary();
ArrayList<Settlement> persistentSettlements = persistentCiv.getSettlements();
// Make sure Civ has pointers to all the necessary elements.
assertTrue(persistentClan != null);
assertTrue(persistentLand != null);
assertTrue(persistentMilitary != null);
assertTrue(persistentMilitary.getArmies() != null);
assertTrue(persistentSettlements != null);
// Lastly we want to make sure that there is only one entry in each of Clan,
// Land, Military, Army, Settlement.
Query q2 = pm.newQuery("SELECT FROM " + Clan.class.getName());
List<Clan> allClans = (List<Clan>) q2.execute();
assertTrue(allClans.size() == 1);
Query q3 = pm.newQuery("SELECT FROM " + Land.class.getName());
List<Land> allLand = (List<Land>) q3.execute();
assertTrue(allLand.size() == 1);
Query q4 = pm.newQuery("SELECT FROM " + Military.class.getName());
List<Military> allMilitary = (List<Military>) q4.execute();
assertTrue(allMilitary.size() == 1);
Query q5 = pm.newQuery("SELECT FROM " + Army.class.getName());
List<Army> allArmy = (List<Army>) q5.execute();
// *** THIS FAILS 50% OF THE TIME ***
assertTrue(allArmy.size() == 1);
Query q6 = pm.newQuery("SELECT FROM " + Settlement.class.getName());
List<Settlement> allSettlement = (List<Settlement>) q6.execute();
assertTrue(allSettlement.size() == 1);
}
}
CivilizationFactory.java :
package com.moffett.grunzke.server;
import java.util.ArrayList;
import com.google.appengine.api.users.User;
import com.moffett.grunzke.server.civilization.Army;
import com.moffett.grunzke.server.civilization.Civilization;
import com.moffett.grunzke.server.civilization.Clan;
import com.moffett.grunzke.server.civilization.Land;
import com.moffett.grunzke.server.civilization.Military;
import com.moffett.grunzke.server.civilization.Settlement;
public class CivilizationFactory
{
public static Civilization newInstance(User user, String clanName, String rulerName)
{
// First we make a new clan.
Clan clan = new Clan();
clan.setUser(user);
clan.setClanName(clanName);
clan.setRulerName(rulerName);
// Don't need land.save() because of persistence-by-reachability
// Now we need to make a new Land.
Land land = new Land();
land.setArableLand(100);
land.setPasturableLand(0);
land.setLandUsedBySettlements(0);
// Don't need land.save() because of persistence-by-reachability
// Now we need to make a new Military
Military military = new Military();
Army army = new Army();
army.setMeleeUnits(10);
army.setRangedUnits(10);
army.setMountedUnits(10);
military.addArmy(army);
// Don't need military.save() because of persistence-by-reachability
// Now we need to make a new Settlement
Settlement settlement = new Settlement();
// Don't need settlement.save() because of persistence-by-reachability
ArrayList<Settlement> settlements = new ArrayList<Settlement>();
settlements.add(settlement);
// Lastly join everything together in the civ
Civilization civ = new Civilization();
civ.setClan(clan);
civ.setLand(land);
civ.setMilitary(military);
civ.setSettlements(settlements);
civ.save();
// civ.save should casacde to cover all of the elements above
return civ;
}
}
Civilization.java :
package com.moffett.grunzke.server.civilization;
import java.util.ArrayList;
import javax.jdo.PersistenceManager;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
import com.moffett.grunzke.server.PMF;
@PersistenceCapable
public class Civilization
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private Clan clan;
@Persistent
private Land land;
@Persistent
private Military military;
@Persistent
private ArrayList<Settlement> settlements = new ArrayList<Settlement>();
public void save()
{
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
pm.makePersistent(this);
}
finally
{
pm.close();
}
}
public ArrayList<Settlement> getSettlements()
{
return settlements;
}
public void setSettlements(ArrayList<Settlement> settlements)
{
this.settlements = settlements;
}
public Key getKey()
{
return key;
}
public void setKey(Key key)
{
this.key = key;
}
public Clan getClan()
{
return clan;
}
public void setClan(Clan clan)
{
this.clan = clan;
}
public Land getLand()
{
return land;
}
public void setLand(Land land)
{
this.land = land;
}
public void setMilitary(Military military)
{
this.military = military;
}
public Military getMilitary()
{
return military;
}
}
Military.java
package com.moffett.grunzke.server.civilization;
import java.util.ArrayList;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
@PersistenceCapable
public class Military
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private ArrayList<Army> armies = new ArrayList<Army>();
public Key getKey()
{
return key;
}
public void setKey(Key key)
{
this.key = key;
}
public ArrayList<Army> getArmies()
{
return armies;
}
public void setArmies(ArrayList<Army> armies)
{
this.armies = armies;
}
public void addArmy(Army army)
{
this.armies.add(army);
}
}
,536,913,632 10 Army.java
package com.moffett.grunzke.server.civilization;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
@PersistenceCapable
public class Army
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private int meleeUnits;
@Persistent
private int rangedUnits;
@Persistent
private int mountedUnits;
public Key getKey()
{
return key;
}
public void setKey(Key key)
{
this.key = key;
}
public int getMeleeUnits()
{
return meleeUnits;
}
public void setMeleeUnits(int meleeUnits)
{
this.meleeUnits = meleeUnits;
}
public int getRangedUnits()
{
return rangedUnits;
}
public void setRangedUnits(int rangeUnits)
{
this.rangedUnits = rangeUnits;
}
public int getMountedUnits()
{
return mountedUnits;
}
public void setMountedUnits(int mountedUnits)
{
this.mountedUnits = mountedUnits;
}
}
보안 점은 매우 유용했다 :
는 개인적으로, 나는 당신의 실체를 변경 방법은 작업 당신이 원하는 노출 할 것이다. 내 샌드 박스 작업에서, 나는 그것을 정말로 고려하지 않았다. 그러나 clear() -> addAll() 전략으로 전환 한 후 내 테스트가 일관되게 실패하기 시작했는데 실제로는 진행으로 간주됩니다. 그런 다음 factory 메서드에 army.save()를 추가 했으므로 이제는 일관되게 전달됩니다. 내 가장 좋은 추측은 간접적 인 의미는 군대가 더 이상 접근 할 수 없다는 것을 의미하기 때문에 나는 그것을 스스로 지켜야합니다. 나는 비 결정적 행동에 대해 여전히 혼란 스럽지만 이것은 효과적이며 더 효과적이다. –
그것은 수수께끼입니다. 목록에서 가져온 모든 메소드를 변경하여 목록의 내용을 엔티티의 목록으로 복사 했습니까? CivilizationFactory.newInstance()를 호출 한 후 테스트에서 지속성 관리자를 만든 경우 다른 동작을 보일지 궁금합니다. http://stackoverflow.com/questions/4185382/how-to-use-jdo-persistence-manager/4186072#4186072 – NamshubWriter
엔티티에 자식이있는 경우이 페이지를 기반으로 트랜잭션에서'makePersistent'를 호출하려고합니다 http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html#Relationships_Entity_Groups_and_Transactions (시도해 보았지만 실제로 엔티티에서 컬렉션 필드를 수정 한 경우) – NamshubWriter