Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA/EclipseLink急切获取,数据未填充(在多个并发查询期间)_Java_Jpa_Concurrency_Many To Many_Eclipselink - Fatal编程技术网

Java JPA/EclipseLink急切获取,数据未填充(在多个并发查询期间)

Java JPA/EclipseLink急切获取,数据未填充(在多个并发查询期间),java,jpa,concurrency,many-to-many,eclipselink,Java,Jpa,Concurrency,Many To Many,Eclipselink,我们试图在启动时将一些实体加载到类中。我们正在加载的实体(LocationGroup实体)与另一个实体(Location实体)具有@ManyToMany关系,该关系由联接表(Location\u GROUP\u MAP)定义。这种关系应该得到热切的关注 对于单个线程,或者当执行JPA查询的方法被同步时,这种方法可以很好地工作。但是,当有多个线程都异步执行JPA查询时(都通过同一个单例DAO类),我们会得到位置集合数据,在某些情况下,这些数据应该被急切地获取,并保留为空 我们在Glassfish

我们试图在启动时将一些实体加载到类中。我们正在加载的实体(LocationGroup实体)与另一个实体(Location实体)具有@ManyToMany关系,该关系由联接表(Location\u GROUP\u MAP)定义。这种关系应该得到热切的关注

对于单个线程,或者当执行JPA查询的方法被同步时,这种方法可以很好地工作。但是,当有多个线程都异步执行JPA查询时(都通过同一个单例DAO类),我们会得到位置集合数据,在某些情况下,这些数据应该被急切地获取,并保留为空

我们在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代码是这样的(我省略了getter/setter和hashCode,equals,toString from Entities-这些实体是通过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;
        }
    }
}
LocationGroupDaoLocal.java

@Local
public interface LocationGroupDaoLocal {
    public List<LocationGroup> getLocationGroupList();
    public List<LocationGroup> getLocationGroupList(LocationGroupType groupType);
}
但是,稍后在同一运行期间完成执行的线程没有NullPointerException:

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实体,除非所有急切获取的相关实体都已加载


顺便说一句,我也尝试过惰性抓取-我遇到了类似的问题,要执行的前几个线程显示位置集合未初始化,然后在某个时刻它会被初始化,所有后续线程都会按预期工作。

我认为从多个线程访问单个应用程序管理的
EntityManager
是无效的

将其设置为容器管理的事务范围:

@PersistenceContext(unitName = "DataAccess-ejb") 
protected EntityManager entityManager; 
或者为每个线程创建一个单独的
EntityManager
(在
getLocationGroupList()内部)

编辑:默认情况下,
EntityManager
不是线程安全的。唯一的例外是容器管理的事务作用域为
EntityManager
,即通过
@PersistenceContext
注入
EntityManager
,而不使用
scope=EXTENDED
。在这种情况下,
EntityManager
充当实际线程本地
EntityManager
s的代理,因此可以从多个线程使用它


有关更多信息,请参见。

的§3.3。您是否有机会解释@PersistenceContext如何防止该问题?由于只注入了一个EntityManager,我假设这意味着每次只在EntityManager上执行一个查询,这可以防止出现上述问题(类似于添加synchronized或使方法@Lock(WRITE))。我假设让这个过程完全并发的唯一方法是使用多个EntityManager实例?再次感谢你的帮助!
@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");
                }
            }
        }
    }
}
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
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
@PersistenceContext(unitName = "DataAccess-ejb") 
protected EntityManager entityManager;