title: JPA / EclipseLink Eager Fetch leaving data unpopulated (during multiple concurrent queries)
body: <p>We are trying to load some entities into our classes on startup. The entities we are loading (LocationGroup entities) have an @ManyToMany relationship with another entity (Location entities), defined by a Join Table (LOCATION_GROUP_MAP). This relationship should be eagerly fetched.</p>
  
  <p>This works fine with a single thread, or when the method performing the JPA query is synchronized. However, when there are multiple threads all performing the JPA query asynchronously (all via the same Singleton DAO class) we get the Location Collection data, which should be Eagerly fetched, being left NULL in some cases.</p>
  
  <p>We are using EclipseLink in Glassfish v3.0.1.</p>
  
  <p>Our database tables (in an Oracle DB) look like this:</p>
  
  <pre><code>LOCATION_GROUP
  location_group_id | location_group_type
  ------------------+--------------------
  GROUP_A           | MY_GROUP_TYPE
  GROUP_B           | MY_GROUP_TYPE
  
  LOCATION_GROUP_MAP
  location_group_id | location_id
  ------------------+------------
  GROUP_A           | LOCATION_01
  GROUP_A           | LOCATION_02
  GROUP_A           | ...
  GROUP_B           | LOCATION_10
  GROUP_B           | LOCATION_11
  GROUP_B           | ...
  
  LOCATION
  location_id
  -----------
  LOCATION_01
  LOCATION_02
  ...
  </code></pre>
  
  <p>And our Java code looks like this (I have omitted getters / setters and hashCode, equals, toString from Entities - the Entities were generated from the DB via NetBeans, then modified slightly, so I don't believe there is any issue with them):</p>
  
  <p>LocationGroup.java:</p>
  
  <pre><code>@Entity
  @Table(name = "LOCATION_GROUP")
  @NamedQueries({
      @NamedQuery(name = "LocationGroup.findAll", query = "SELECT a FROM LocationGroup a"),
      @NamedQuery(name = "LocationGroup.findByLocationGroupId", query = "SELECT a FROM LocationGroup a WHERE a.locationGroupId = :locationGroupId"),
      @NamedQuery(name = "LocationGroup.findByLocationGroupType", query = "SELECT a FROM LocationGroup a WHERE a.locationGroupType = :locationGroupType")})
  public class LocationGroup implements Serializable {
      private static final long serialVersionUID = 1L;
  
      @Id
      @Basic(optional = false)
      @Column(name = "LOCATION_GROUP_ID")
      private String locationGroupId;
  
      @Basic(optional = false)
      @Column(name = "LOCATION_GROUP_TYPE")
      private String locationGroupType;
  
      @JoinTable(name = "LOCATION_GROUP_MAP",
          joinColumns = { @JoinColumn(name = "LOCATION_GROUP_ID", referencedColumnName = "LOCATION_GROUP_ID")},
          inverseJoinColumns = { @JoinColumn(name = "LOCATION_ID", referencedColumnName = "LOCATION_ID")})
      @ManyToMany(fetch = FetchType.EAGER)
      private Collection&lt;Location&gt; locationCollection;
  
      public LocationGroup() {
      }
  
      public LocationGroup(String locationGroupId) {
          this.locationGroupId = locationGroupId;
      }
  
      public LocationGroup(String locationGroupId, String locationGroupType) {
          this.locationGroupId = locationGroupId;
          this.locationGroupType = locationGroupType;
      }
  
      public enum LocationGroupType {
          MY_GROUP_TYPE("MY_GROUP_TYPE");
  
          private String locationGroupTypeString;
  
          LocationGroupType(String value) {
              this.locationGroupTypeString = value;
          }
  
          public String getLocationGroupTypeString() {
              return this.locationGroupTypeString;
          }
      }
  }
  </code></pre>
  
  <p>Location.java</p>
  
  <pre><code>@Entity
  @Table(name = "LOCATION")
  public class Location implements Serializable {
      private static final long serialVersionUID = 1L;
  
      @Id
      @Basic(optional = false)
      @Column(name = "LOCATION_ID")
      private String locationId;
  
      public Location() {
      }
  
      public Location(String locationId) {
          this.locationId = locationId;
      }
  
  }
  </code></pre>
  
  <p>LocationGroupDaoLocal.java</p>
  
  <pre><code>@Local
  public interface LocationGroupDaoLocal {
      public List&lt;LocationGroup&gt; getLocationGroupList();
      public List&lt;LocationGroup&gt; getLocationGroupList(LocationGroupType groupType);
  }
  </code></pre>
  
  <p>LocationGroupDao.java</p>
  
  <pre><code>@Singleton
  @LocalBean
  @Startup
  @Lock(READ)
  public class LocationGroupDao implements LocationGroupDaoLocal {
      @PersistenceUnit(unitName = "DataAccess-ejb")
      protected EntityManagerFactory factory;
  
      protected EntityManager entityManager;
  
      @PostConstruct
      public void setUp() {
          entityManager = factory.createEntityManager();
          entityManager.setFlushMode(FlushModeType.COMMIT);
      }
  
      @PreDestroy
      public void shutdown() {
          entityManager.close();
          factory.close();
      }
  
      @Override
      public List&lt;LocationGroup&gt; getLocationGroupList() {
          TypedQuery query = entityManager.createNamedQuery("LocationGroup.findAll", LocationGroup.class);
          return query.getResultList();
      }
  
      @Override
      public List&lt;LocationGroup&gt; getLocationGroupList(LocationGroupType groupType) {
          System.out.println("LOGGING-" + Thread.currentThread().getName() + ": Creating Query for groupType [" + groupType + "]");
          TypedQuery query = entityManager.createNamedQuery("LocationGroup.findByLocationGroupType", LocationGroup.class);
          query.setParameter("locationGroupType", groupType.getLocationGroupTypeString());
          System.out.println("LOGGING-" + Thread.currentThread().getName() + ": About to Execute Query for groupType [" + groupType + "]");
          List&lt;LocationGroup&gt; results = query.getResultList();
          System.out.println("LOGGING-" + Thread.currentThread().getName() + ": Executed Query for groupType [" + groupType + "] and got [" + results.size() + "] results");
          return results;
      }
  }
  </code></pre>
  
  <p>Manager.java</p>
  
  <pre><code>@Singleton
  @Startup
  @LocalBean
  public class Manager {
      @EJB private LocationGroupDaoLocal locationGroupDao;
  
      @PostConstruct
      public void startup() {
          System.out.println("LOGGING: Starting!");
  
          // Create all our threads
          Collection&lt;GroupThread&gt; threads = new ArrayList&lt;GroupThread&gt;();
          for (int i=0; i&lt;20; i++) {
              threads.add(new GroupThread());
          }
  
          // Start each thread
          for (GroupThread thread : threads) {
              thread.start();
          }
      }
  
      private class GroupThread extends Thread {
          @Override
          public void run() {
              System.out.println("LOGGING-" + this.getName() + ": Getting LocationGroups!");
              List&lt;LocationGroup&gt; locationGroups = locationGroupDao.getLocationGroupList(LocationGroup.LocationGroupType.MY_GROUP_TYPE);
              for (LocationGroup locationGroup : locationGroups) {
                  System.out.println("LOGGING-" + this.getName() + ": Group [" + locationGroup.getLocationGroupId() +
                          "], Found Locations: [" + locationGroup.getLocationCollection() + "]");
  
                  try {
                      for (Location loc : locationGroup.getLocationCollection()) {
                          System.out.println("LOGGING-" + this.getName() + ": Group [" + locationGroup.getLocationGroupId()
                                  + "], Found location [" + loc.getLocationId() + "]");
                      }
                  } catch (NullPointerException npe) {
                      System.out.println("LOGGING-" + this.getName() + ": Group [" + locationGroup.getLocationGroupId()
                              + "], NullPointerException while looping over locations");
                  }
  
                  try {
                      System.out.println("LOGGING-" + this.getName() + ": Group [" + locationGroup.getLocationGroupId()
                          + "], Found [" + locationGroup.getLocationCollection().size() + "] Locations");
                  } catch (NullPointerException npe) {
                      System.out.println("LOGGING-" + this.getName() + ": Group [" + locationGroup.getLocationGroupId()
                              + "], NullPointerException while printing Size of location collection");
                  }
              }
          }
      }
  }
  </code></pre>
  
  <p>So our manager starts up, and then creates 20 threads, all of which call into the Singleton LocationGroupDao concurrently, attempting to load the LocationGroups of type MY_GROUP_TYPE. Both LocationGroups are always returned. However, the Location collection on the LocationGroup (defined by the @ManyToMany relationship) is sometimes NULL when the LocationGroup entities are returned.</p>
  
  <p>If we make the LocationGroupDao.getLocationGroupList(LocationGroupType groupType) method synchronized all is well (we never see the output lines indicating a NullPointerException occurred), and similarly if you change the for loop in Manager.startup() to only have a single iteration (so only one Thread is created / executed).</p>
  
  <p>However, with the code as is, we do get the output lines with NullPointerException, eg (filtering out just the lines for one of the threads):</p>
  
  <pre><code>LOGGING-Thread-172: Getting LocationGroups!
  LOGGING-Thread-172: Creating Query for groupType [MY_GROUP_TYPE]
  LOGGING-Thread-172: About to Execute Query for groupType [MY_GROUP_TYPE]
  LOGGING-Thread-172: Executed Query for groupType [MY_GROUP_TYPE] and got [2] results
  LOGGING-Thread-172: Group [GROUP_A], Found Locations: [null]
  LOGGING-Thread-172: Group [GROUP_A], NullPointerException while looping over locations
  LOGGING-Thread-172: Group [GROUP_A], NullPointerException while printing Size of location collection
  LOGGING-Thread-172: Group [GROUP_B], Found Locations: [null]
  LOGGING-Thread-172: Group [GROUP_B], NullPointerException while looping over locations
  LOGGING-Thread-172: Group [GROUP_B], NullPointerException while printing Size of location collection
  </code></pre>
  
  <p>However, threads that complete execution later during the same run have no NullPointerExceptions:</p>
  
  <pre><code>LOGGING-Thread-168: Getting LocationGroups!
  LOGGING-Thread-168: Creating Query for groupType [MY_GROUP_TYPE]
  LOGGING-Thread-168: About to Execute Query for groupType [MY_GROUP_TYPE]
  LOGGING-Thread-168: Executed Query for groupType [MY_GROUP_TYPE] and got [2] results
  LOGGING-Thread-168: Group [GROUP_A], Found Locations: [...]
  LOGGING-Thread-168: Group [GROUP_A], Found location [LOCATION_01]
  LOGGING-Thread-168: Group [GROUP_A], Found location [LOCATION_02]
  LOGGING-Thread-168: Group [GROUP_A], Found location [LOCATION_03]
  ...
  LOGGING-Thread-168: Group [GROUP_A], Found [8] Locations
  LOGGING-Thread-168: Group [GROUP_B], Found Locations: [...]
  LOGGING-Thread-168: Group [GROUP_B], Found location [LOCATION_10]
  LOGGING-Thread-168: Group [GROUP_B], Found location [LOCATION_11]
  LOGGING-Thread-168: Group [GROUP_B], Found location [LOCATION_12]
  ...
  LOGGING-Thread-168: Group [GROUP_B], Found [11] Locations
  </code></pre>
  
  <p>Definitely appears to be a concurrency issue, but I don't see why the LocationGroup entities are returned unless all their Eagerly Fetched related entities have been loaded.</p>
  
  <p>As a side note, I have tried this with Lazy fetching too - I get a similar problem, the first few threads to execute show that the Location collection is uninitialized, and then at some point it becomes initialized and all later threads work as expected.</p>