GWT 和 JPA 并发,@PersistenceContext,NullPointerException

GWT and JPA concurrency, @PersistenceContext, NullPointerException

我有一个纯粹的 JAVA 项目,我在 eclipse 中使用 maven 进行开发。它具有使用 JPA 和 EclipseLink 的持久性功能,可以将数据保存到 Apache Derby 中。该项目在单元测试和独立 java 应用程序中完美运行,在这些应用程序中,我直接从我的代码中实例化 EntityManagerFactory:

public class JPAUtil
{
    private static EntityManagerFactory     factory                 = Persistence.createEntityManagerFactory("unit-name");
    private static Map<Long, EntityManager> ems                     = new HashMap<Long, EntityManager>();

    private JPAUtil(){}

    /**
     * Get an entity manager
     */
    public static EntityManager em(Long id)
    {       
        EntityManager result = null;
        if (ems.containsKey(id))
        {
            result = ems.get(id);
            if(!result.isOpen())
            {
                result = createEntityManager();
                ems.put(id, result);
            }
        }
        else
        {
            result = createEntityManager();
            ems.put(id, result);
        }

        return result;
    }

    private static EntityManager createEntityManager()
    {
        EntityManager result =
                    // factory.createEntityManager(SynchronizationType.SYNCHRONIZED);
                    factory.createEntityManager();
        return result;
    }
}

现在,当我将它添加到 GWT 项目中时,我遇到了一些非常难以 debug/solve 的问题。

问题 1:

如果我使用上面的 JPAUtil class 实例化 EntityManagers 以用于每个 RPC 请求,它就可以工作。但是,如果 GWT 客户端开始向服务器端发出多个请求,而服务器端又试图从 JPA 层拉取数据,则读取时会发生多个神秘的 ConcurrencyException(有或没有延迟加载 - 似乎没有区别) .

当我不使用上述 class 时,我尝试 "inject" EntityManager 使用以下行进入 GWT ServiceImpls (servlet),尝试访问数据层时因 NullPointerException 而崩溃:

@PersistenceContext(unitName = "unit-name")
transient protected EntityManager   em;

我显然在想,从 GWT 访问持久层将是一种更合适的方式。但是,在访问 EntityManager 时出现 NullPointerExceptions,因此 GWT 的开发 Jetty 服务器无法自行注入 EntityManager。这种问题看来我的能力有限,我的Google-FU好像也无能为力。所以制定一个具体的问题:

如何最好地解决在后端使用 JPA 创建快速、稳定的 GWT 应用程序的问题?

提前谢谢你, el.nicko

您需要同步对 HashMap 的访问,因为许多 RPC 请求可以由多个线程并行处理。建议您将 HashMap 替换为 ConcurrentHashMap 或将同步放在 em 方法上。

@Inject 可能无法工作,因为 GWT servlet 不支持 CDI。