【问题标题】:Guice injected EntityManager in multi threaded applicationGuice 在多线程应用程序中注入 EntityManager
【发布时间】:2026-02-06 23:30:01
【问题描述】:

我正在使用 Java SE 7 开发桌面应用程序。该应用程序使用多个线程,并且在创建的每个线程中注入一个 DAO 类以访问我的数据库。作为持久层,我使用的是 EclipseLink 和 JPA。 EntityManager 使用构造函数注入注入到我的 DAO 类中,并且由于它不是线程安全的,因此我采用了这样的 Provder 方法:

public PluginInstanceJpaController implements IPluginInstanceDao {

    private EntityManager em;

    @Injected
    public PluginInstanceJpaController(Provider<EntityManager> emp) {
        this.em = emp.get();
    }

    @Transactional 
    public void create(PluginInstance foo) throws Exception {
        em.persist(foo);
    }
}

但是,相同的 EntityManager 实例被注入到每个 DAO 中。为了进行设置,我使用了 guice 提供的 JpaPersistModule,我确信到目前为止我的设置中没有单例。

有谁知道在注入时如何告诉 guice 创建 EntityManager 的新实例?

在另一种方法中,我尝试了 EntityManagerFactory 和 EntityManager 的自定义提供程序,并将 JpaPersistModule 排除在我的业务之外。这导致每个 DAO 都有一个 EntityManager 实例,但是 @Transactional 注释的方法没有被拦截。

我很感激这个问题的任何解决方案。 到目前为止谢谢!

--- 编辑 ---

DAO 类被注入到使用它们的 Runnable 中。 Runnable 也通过 Provider 提供。我的模块配置如下所示:

public class RepositoryModule extends AbstractModule {

    @Override
    protected void configure() {

        // DAO bindings
        bind(IObjectStoreDao.class).to(ObjectStoreJpaController.class);
        bind(IPluginInstanceDao.class).to(PluginInstanceJpaController.class);
    }

    @Provides
    public PluginMonitor newMonitor(IPluginInstanceDao plugDao, IObjectStoreDao osDao) {
        PluginMonitor m = new PluginMonitor();
        m.setPluginInstanceDao(plugDao);
        m.setObjectStoreDao(osDao);
        return m;
    }
}

这里的 PluginMonitor 是我的 Runnable。 Injector 本身是在我的主线程中创建的……这可能是问题所在吗?

【问题讨论】:

    标签: java jpa guice


    【解决方案1】:

    这是一个非常相似的问题:How Guice injects singletons and non-singletons into multiple threads

    对你的 DAO 来说,这应该可行。

    public PluginInstanceJpaController implements IPluginInstanceDao {
    
        private Provider<EntityManager> emProvider;
    
        @Injected
        public PluginInstanceJpaController(Provider<EntityManager> emp) {
            this.em = emp;
        }
    
        @Transactional 
        public void create(PluginInstance foo) throws Exception {
            em.get().persist(foo);
        }
    }
    

    您应该使用Jpa Persistence Module 或创建自定义EntityManager 提供程序,它将为每个get() 调用返回新的EntityManager,也可以使用ThreadLocal 来实现,以确保EntityManager 将在线程间共享。

    【讨论】:

      【解决方案2】:

      我不熟悉 JPA,但希望我仍然可以提供帮助 :-)

      如果您查看the source for EntityManagerProvider,您会看到有一个ThreadLocal&lt;EntityManager&gt;。所以默认情况下,每个线程都应该有自己的EntityManager。这让我相信问题出在其他地方。您确定没有模块将EntityManager 设置为单例吗?您如何确定所有 EntityManagers 都是同一个对象?每个 DAO 肯定都在自己的线程上?您能否详细说明 FooDao 在您的模块中是如何配置的,以及如何为每个线程提供一个新的 FooDao?

      此外,您应该可以将构造函数编写为:

      @Inject
      public FooDao(EntityManager emp) {
          this.em = emp;
      }
      

      Guice 将帮助您确定EntityManagerProvider 提供EntityManager 实例,并将在EntityManagerProvider 实例上调用get() 以获取EntityManager 以提供给您的构造函数。

      【讨论】:

      • 嗨!我使用的是 eclipse 调试器,它在每个线程中的任何 EntityManager 引用上都给了我相同的调试 ID,所以如果我被正确地告知这个引用是同一个实例。 DAO 由提供 runnable(我实际的 Thread Runnable)的 Provider 注入。在为此苦苦挣扎了几个小时后,我决定摆脱困境,走自己的路。然而,也许这个讨论可能会对某人有所帮助。