title: Inconsistent cascading persistence-by-reachability behavior with JDO, App Engine, Data Nucleus, JUnit
body: <p>I'm experimenting with App Engine, using JDO and DataNucleus for persistence.  I have a simple domain that includes several unidirectional relationships.  The question comes with nesting those relationships:</p>
  
  <ul>
  <li>Civilization -(1-1)-> Clan</li>
  <li>Civilization -(1-1)-> Land</li>
  <li>Civilization -(1-1)-> Military -(1-N)-> Armies (this is inconsistent)</li>
  <li>Civilization -(1-N)-> Settlement</li>
  </ul>
  
  <p>According to the <a href="http://www.datanucleus.org/products/accessplatform/jdo/orm/cascading.html" rel="nofollow">DataNucleus Documentation</a>, persistence-by-reachability semantics should persist everything by cascading on a persist of a Civilization.  I have a JUnit test to check the basic storage and retrieval of these objects, but its behavior is inconsistent.  With no changes to the code, repeated runs of the test give nondeterministic results.  Specifically, the armies only persist about 50% of the time.  They are the only test that fails.</p>
  
  <p>I could more easily understand a scenario where Armies never persist, but the irregular behavior has me at a loss.  Everything else persists correctly and consistently.  I've tried wrapping the factory method in a transaction and I've tried bidirectional relationships, and neither have changed the 50/50 pass/fail split in JUnit.</p>
  
  <p>I am using Annotation-based configuration for DataNucleus, as described in the App Engine documentation (link not included because of anti-spam measures).  I apologize for the large amount of code attached; I just don't know where I'm going wrong.</p>
  
  <p>CivilizationCreateTest.java:</p>
  
  <pre><code>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("generic.user@gmail.com")
                                                  .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&lt;Civilization&gt; allCivilizations = (List&lt;Civilization&gt;) 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&lt;Settlement&gt; 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&lt;Clan&gt; allClans = (List&lt;Clan&gt;) q2.execute();
  
      assertTrue(allClans.size() == 1);
  
      Query q3 = pm.newQuery("SELECT FROM " + Land.class.getName());
      List&lt;Land&gt; allLand = (List&lt;Land&gt;) q3.execute();
  
      assertTrue(allLand.size() == 1);
  
      Query q4 = pm.newQuery("SELECT FROM " + Military.class.getName());
      List&lt;Military&gt; allMilitary = (List&lt;Military&gt;) q4.execute();
  
      assertTrue(allMilitary.size() == 1);
  
      Query q5 = pm.newQuery("SELECT FROM " + Army.class.getName());
      List&lt;Army&gt; allArmy = (List&lt;Army&gt;) q5.execute();
  
      // *** THIS FAILS 50% OF THE TIME ***
      assertTrue(allArmy.size() == 1);
  
      Query q6 = pm.newQuery("SELECT FROM " + Settlement.class.getName());
      List&lt;Settlement&gt; allSettlement = (List&lt;Settlement&gt;) q6.execute();
  
      assertTrue(allSettlement.size() == 1);
  
    }
  
  }
  </code></pre>
  
  <p>CivilizationFactory.java:</p>
  
  <pre><code>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&lt;Settlement&gt; settlements = new ArrayList&lt;Settlement&gt;();
      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;
    }
  
  }
  </code></pre>
  
  <p>Civilization.java:</p>
  
  <pre><code>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&lt;Settlement&gt; settlements = new ArrayList&lt;Settlement&gt;();
  
    public void save()
    {
      PersistenceManager pm = PMF.get().getPersistenceManager();
      try
      {
        pm.makePersistent(this);
      }
      finally
      {
        pm.close();
      }
    }
  
    public ArrayList&lt;Settlement&gt; getSettlements()
    {
      return settlements;
    }
  
    public void setSettlements(ArrayList&lt;Settlement&gt; 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;
    }
  }
  </code></pre>
  
  <p>Military.java</p>
  
  <pre><code>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&lt;Army&gt; armies = new ArrayList&lt;Army&gt;();
  
    public Key getKey()
    {
      return key;
    }
  
    public void setKey(Key key)
    {
      this.key = key;
    }
  
    public ArrayList&lt;Army&gt; getArmies()
    {
      return armies;
    }
  
    public void setArmies(ArrayList&lt;Army&gt; armies)
    {
      this.armies = armies;
    }
  
    public void addArmy(Army army)
    {
      this.armies.add(army);
    }
  
  }
  </code></pre>
  
  <p>Army.java</p>
  
  <pre><code>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;
    }
  }
  </code></pre>