java - JPA / EclipseLink Eager Fetch leaving data unpopulated (during multiple concurrent queries) -


we trying load entities our classes on startup. entities loading (locationgroup entities) have @manytomany relationship entity (location entities), defined join table (location_group_map). relationship should eagerly fetched.

this works fine single thread, or when method performing jpa query synchronized. however, when there multiple threads performing jpa query asynchronously (all via same singleton dao class) location collection data, should eagerly fetched, being left null in cases.

we using eclipselink in glassfish v3.0.1.

our database tables (in oracle db) this:

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 ... 

and our java code looks (i have omitted getters / setters , hashcode, equals, tostring entities - entities generated db via netbeans, modified slightly, don't believe there issue them):

locationgroup.java:

@entity @table(name = "location_group") @namedqueries({     @namedquery(name = "locationgroup.findall", query = "select locationgroup a"),     @namedquery(name = "locationgroup.findbylocationgroupid", query = "select locationgroup a.locationgroupid = :locationgroupid"),     @namedquery(name = "locationgroup.findbylocationgrouptype", query = "select locationgroup 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<location> 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;         }     } } 

location.java

@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;     }  } 

locationgroupdaolocal.java

@local public interface locationgroupdaolocal {     public list<locationgroup> getlocationgrouplist();     public list<locationgroup> getlocationgrouplist(locationgrouptype grouptype); } 

locationgroupdao.java

@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<locationgroup> getlocationgrouplist() {         typedquery query = entitymanager.createnamedquery("locationgroup.findall", locationgroup.class);         return query.getresultlist();     }      @override     public list<locationgroup> getlocationgrouplist(locationgrouptype grouptype) {         system.out.println("logging-" + thread.currentthread().getname() + ": creating query grouptype [" + grouptype + "]");         typedquery query = entitymanager.createnamedquery("locationgroup.findbylocationgrouptype", locationgroup.class);         query.setparameter("locationgrouptype", grouptype.getlocationgrouptypestring());         system.out.println("logging-" + thread.currentthread().getname() + ": execute query grouptype [" + grouptype + "]");         list<locationgroup> results = query.getresultlist();         system.out.println("logging-" + thread.currentthread().getname() + ": executed query grouptype [" + grouptype + "] , got [" + results.size() + "] results");         return results;     } } 

manager.java

@singleton @startup @localbean public class manager {     @ejb private locationgroupdaolocal locationgroupdao;      @postconstruct     public void startup() {         system.out.println("logging: starting!");          // create our threads         collection<groupthread> threads = new arraylist<groupthread>();         (int i=0; i<20; i++) {             threads.add(new groupthread());         }          // start each thread         (groupthread thread : threads) {             thread.start();         }     }      private class groupthread extends thread {         @override         public void run() {             system.out.println("logging-" + this.getname() + ": getting locationgroups!");             list<locationgroup> locationgroups = locationgroupdao.getlocationgrouplist(locationgroup.locationgrouptype.my_group_type);             (locationgroup locationgroup : locationgroups) {                 system.out.println("logging-" + this.getname() + ": group [" + locationgroup.getlocationgroupid() +                         "], found locations: [" + locationgroup.getlocationcollection() + "]");                  try {                     (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 on 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");                 }             }         }     } } 

so our manager starts up, , creates 20 threads, of call singleton locationgroupdao concurrently, attempting load locationgroups of type my_group_type. both locationgroups returned. however, location collection on locationgroup (defined @manytomany relationship) null when locationgroup entities returned.

if make locationgroupdao.getlocationgrouplist(locationgrouptype grouptype) method synchronized (we never see output lines indicating nullpointerexception occurred), , if change loop in manager.startup() have single iteration (so 1 thread created / executed).

however, code is, output lines nullpointerexception, eg (filtering out lines 1 of threads):

logging-thread-172: getting locationgroups! logging-thread-172: creating query grouptype [my_group_type] logging-thread-172: execute query grouptype [my_group_type] logging-thread-172: executed query grouptype [my_group_type] , got [2] results logging-thread-172: group [group_a], found locations: [null] logging-thread-172: group [group_a], nullpointerexception while looping on 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 on locations logging-thread-172: group [group_b], nullpointerexception while printing size of location collection 

however, threads complete execution later during same run have no nullpointerexceptions:

logging-thread-168: getting locationgroups! logging-thread-168: creating query grouptype [my_group_type] logging-thread-168: execute query grouptype [my_group_type] logging-thread-168: executed query grouptype [my_group_type] , 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 

definitely appears concurrency issue, don't see why locationgroup entities returned unless eagerly fetched related entities have been loaded.

as side note, have tried lazy fetching - similar problem, first few threads execute show location collection uninitialized, , @ point becomes initialized , later threads work expected.

i don't think it's valid access single application-managed entitymanager multiple threads.

either make container-managed transaction-scoped:

@persistencecontext(unitname = "dataaccess-ejb")  protected entitymanager entitymanager;  

or create separate entitymanager each thread (inside getlocationgrouplist()).

edit: default entitymanager not thread-safe. exception container-managed transaction-scoped entitymanager, entitymanager injected via @persistencecontext without scope = extended. in case entitymanager acts proxy actual thread-local entitymanagers, therefore can used multiple threads.

for more information see §3.3 of jpa specification.


Comments

Popular posts from this blog

asp.net - repeatedly call AddImageUrl(url) to assemble pdf document -

java - Android recognize cell phone with keyboard or not? -

iphone - How would you achieve a LED Scrolling effect? -