起動時にいくつかのエンティティをクラスにロードしようとしています。私たちがロードしているエンティティ(LocationGroupエンティティ)は、Join Table(LOCATION_GROUP_MAP)によって定義された別のエンティティ(Locationエンティティ)と@ManyToMany関係を持っています。この関係は熱心に取り出されるべきです。JPA/EclipseLink Eagerフェッチ・データを残しておく(複数の同時問合せ中)
これは、単一のスレッドで、またはJPAクエリを実行するメソッドが同期している場合に問題ありません。しかし、すべてがJPAクエリを非同期的に(同じSingleton DAOクラスを介して)実行している複数のスレッドがある場合、私たちはEagerlyフェッチされるLocation Collectionデータを得ることがあります。
GlassFish v3.0.1でEclipseLinkを使用しています。 (OracleのDBで)
当社のデータベーステーブルは、次のようになります。
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
...
そして、我々のJavaコードは次のようになります(私は、ゲッター/セッターとhashCodeを省略等しく、エンティティからのtoStringいる - エンティティでしたその後、)わずかに変更されたので、私は彼らと何らかの問題があるとは思わない、NetBeansのを経由してDBから生成さ:
LocationGroup.java:
@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<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;
}
}
}
場所。 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 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<LocationGroup> results = query.getResultList();
System.out.println("LOGGING-" + Thread.currentThread().getName() + ": Executed Query for groupType [" + groupType + "] and 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 all our threads
Collection<GroupThread> threads = new ArrayList<GroupThread>();
for (int i=0; i<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<LocationGroup> 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");
}
}
}
}
}
は、だから、私たちのマネージャーが起動し、その後、20個のスレッドを作成しますこれらのすべてがtに呼び出されます彼はSingleton LocationGroupDaoを同時に実行し、タイプMY_GROUP_TYPEのLocationGroupsをロードしようとしました。両方のLocationGroupが常に返されます。ただし、LocationGroupエンティティが返されると、@ManyToMany関係で定義されたLocationGroupのLocationコレクションがNULLになることがあります。
LocationGroupDao.getLocationGroupList(LocationGroupType groupType)メソッドを同期させると、すべてが正常です(NullPointerExceptionが発生したことを示す出力行は表示されません)。同様に、Manager.startup()のforループを変更するだけで、単一の反復(したがって、1つのスレッドのみが作成/実行されます)。
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
しかし、完全な実行後に中のスレッド:例えば、コードのように、我々は、NullPointerExceptionで出力ラインを得るか、(スレッドのいずれかの行だけをフィルタリング)とただし
、同じ実行一切NullPointerExceptionsがを持っていない:
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
は確かに同時実行の問題のように見えるが、すべての彼らの即時にフェッチに関連するエンティティがロードされていない限りLocationGroupエンティティが返されますなぜ私は表示されません。
私はこれをLazyフェッチでも試してみました。同様の問題が発生しました。実行する最初のいくつかのスレッドは、Locationコレクションが初期化されていないことを示しています。期待どおりに動作します。
ありがとうございました。 –
@PersistenceContextが問題をどのように防止するか説明できますか? 1つのEntityManagerしかインジェクトされていないので、EntityManagerで一度に1つのクエリしか実行されないことを前提としています(同期の追加や@Lock(WRITE)のメソッドの作成と同様)。 このプロセスを完全に並行させる唯一の方法は、複数のEntityManagerインスタンスを持つルートを辿るということですか? もう一度お世話になります! –