【问题标题】:JPA EntityManager not working when using Guice's PrivateModule使用 Guice 的 PrivateModule 时 JPA EntityManager 不起作用
【发布时间】:2021-03-16 21:48:46
【问题描述】:

我有一个使用 JPA、Hibernate 和 Guice 设置持久性的服务(如果有用,我没有使用 Spring)。这是我的代码的第一个工作版本:

public class BookDao {

    @Inject
    protected Provider<EntityManager> entityManagerProvider;

    protected EntityManager getEntityManager() {
        return entityManagerProvider.get();
    }

    @Transactional
    public void persist(Book book) {
        getEntityManager().persist(book);
    }

}

public class MyAppModule extends AbstractModule {

    @Override
    protected void configure() {
        initializePersistence();
    }

    private void initializePersistence() {
        final JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
        jpaPersistModule.properties(new Properties());
        install(jpaPersistModule);
    }

}

但是现在我需要配置多个持久化单元。我正在遵循mailing list 中的建议,根据他们的说法,我应该将我的模块逻辑移动到一个私有模块。我按照建议做了,并创建了相同代码的第二个版本,更改如下:

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ FIELD, PARAMETER, METHOD })
public @interface ProductionDataSource {} // defined this new annotation

public class BookDao {

    @Inject
    @ProductionDataSource // added the annotation here
    protected Provider<EntityManager> entityManagerProvider;

    protected EntityManager getEntityManager() {
        return entityManagerProvider.get();
    }

    @Transactional
    public void persist(Book book) throws Exception {
        getEntityManager().persist(book);
    }

}

public class MyAppModule extends PrivateModule { // module is now private

    @Override
    protected void configure() {
        initializePersistence();
        // expose the annotated entity manager
        Provider<EntityManager> entityManagerProvider = binder().getProvider(EntityManager.class);
        bind(EntityManager.class).annotatedWith(ProductionDataSource.class).toProvider(entityManagerProvider);
        expose(EntityManager.class).annotatedWith(ProductionDataSource.class);

    }

    private void initializePersistence() {
        JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
        jpaPersistModule.properties(new Properties());
        install(jpaPersistModule);
    }

}

新注释的 EntityManager 被 Guice 正确注入并且是非空的,但有趣的是:我的一些单元测试开始失败,例如:

class BookDaoTest {

    private Injector injector;
    private BookDao testee;

    @BeforeEach
    public void setup() {
        injector = Guice.createInjector(new MyAppModule());
        injector.injectMembers(this);
        testee = injector.getInstance(BookDao.class);
    }

    @Test
    public void testPersistBook() throws Exception {
        // given
        Book newBook = new Book();
        assertNull(newBook.getId());
        // when
        newBook = testee.persist(newBook);
        // then
        assertNotNull(newBook.getId()); // works in the first version, fails in the second
    }

}

在我的代码的第一个版本中,上面的最后一行有效:实体被持久化并具有新的 id。但是,在我的代码的第二个版本中(使用PrivateModule 并从中公开带注释的EntityManagerpersist() 操作不再起作用,实体没有ID。可能是什么问题呢?我没有在我的环境中进行任何其他配置更改,也没有在日志中看到错误消息。如果您需要更多详细信息,请告诉我。

【问题讨论】:

    标签: java jpa dependency-injection orm guice


    【解决方案1】:

    原来问题出在@Transactional 注释上。在我的代码的第一个版本中,Guice 自动添加了用于管理事务的拦截器。通过调试,我发现执行我的persist(Book book) 方法之前,Guice 从com.google.inject.internal.InterceptorStackCallback 包中调用了以下方法:

    public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
    

    在我的代码的第二个版本中,当我从私有模块中公开持久性单元时,不再调用上述拦截器,使我的持久性操作没有事务处理。这是knownissue,是设计使然。

    作为一种解决方法,我必须手动实现事务,使我的代码更加冗长。我还必须改变注入实体管理器的方式。这个解决方案对我有用:

    public class BookDao {
    
        @Inject
        @Named(PROD_PERSISTENCE_UNIT_NAME)
        private EntityManagerFactory entityManagerFactory;
    
        private EntityManager getEntityManager() {
            return entityManagerFactory.createEntityManager();
        }
    
        public void persist(Book book) throws Exception {
            EntityManager em = getEntityManager();
            try {
                em.getTransaction().begin();
                em.persist(book);
                em.getTransaction().commit();
            } catch (Exception e) {
                em.getTransaction().rollback();
                throw e;
            } finally {
                em.close();
            }
        }
    
    }
    
    public class MyAppModule extends PrivateModule {
    
        public static final String PROD_PERSISTENCE_UNIT_NAME = "prod";
    
        @Override
        protected void configure() {
            initializePersistence();
        }
    
        private void initializePersistence() {
            // persistence unit set to prod DB
            final JpaPersistModule jpaPersistModule = new JpaPersistModule(PROD_PERSISTENCE_UNIT_NAME);
            // connection properties set to suitable prod values
            jpaPersistModule.properties(new Properties());
            install(jpaPersistModule);
            // expose bindings to entity manager annotated as "prod"
            bind(JPAInitializer.class).asEagerSingleton();
            bind(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).to(PersistService.class).asEagerSingleton();
            expose(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
            bind(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManagerFactory.class));
            expose(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
            bind(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManager.class));
            expose(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
            bind(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(UnitOfWork.class));
            expose(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
        }
    
    }
    

    作为一个教训,要非常注意注释和其他类似“魔法”,它们会在后台修改你的代码,发现错误变得非常困难。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-08
      • 1970-01-01
      • 2016-09-21
      • 1970-01-01
      • 2015-05-01
      • 2021-08-30
      • 1970-01-01
      • 2017-02-12
      相关资源
      最近更新 更多