首页 > 解决方案 > 两个不同 LocalContainerEntityManagerFactoryBean 上的 @PersistenceContext 抛出 TransactionRequiredException

问题描述

LocalContainerEntityManagerFactoryBean设置了两个,一个有@Primary注释,一个没有。这是因为我必须与两个不同的数据库进行通信,如果我删除@Primary装饰,我会得到一个重复的 bean 错误。两者的构造方式完全相同,这里有一个供参考:

package com.myorg.rest.config.dba;

import ...

@Configuration
@EnableJpaRepositories(
    basePackages = "com.myorg.rest.dao.dbA",
    entityManagerFactoryRef = "dbAEntityManager",
    transactionManagerRef = "dbATransactionManager"
    )
@EnableTransactionManagement
public class DbADataSourceConfig {

    @Autowired
    private EnvProperties settings;

    @Bean
    @Primary
    public DataSource prjDataSource() {
        DataSourceProperties ds = settings.getdbADatasource();
        return ds.initializeDataSourceBuilder().type(BasicDataSource.class).build();
    }

    @Bean(name = "dbAEntityManager")
    @Primary
    public LocalContainerEntityManagerFactoryBean dbAEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(prjDataSource());
        em.setPackagesToScan(new String[] { "com.myorg.model.entities.dba" });
        em.setPersistenceUnitName("dbAUnit");

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());
        // em.afterPropertiesSet();

        return em;
    }

    @Bean(name = "dbATransactionManager")
    @Primary
    public PlatformTransactionManager dbATransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(dbAEntityManager().getObject());

        return transactionManager;
    }

    Properties additionalProperties() {
        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        return properties;
    }
}

它们都使用 和 注入到 DAO 中@PersistenceContext(unitName = "dbAUnit")@PersistenceContext(unitName = "dbBUnit")并且它们都成功地引导了内部相应的 EntityManager。我可以在它们两个中创建,然后检索创建的实体。但是,当我使用以下内容检索所有实体时:

public List<T> findAll() {
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<T> entityQuery = criteriaBuilder.createQuery(clazz);
    entityQuery.from(clazz);
    return entityManager.createQuery(entityQuery).getResultList();
}

其中一个可以完美运行,而另一个则不能(它返回 0 个实体)。经过调试,我意识到其中一个发出了insert into...sql 命令,而另一个没有发出。我试图强迫entityManager.flush()并得到

 Caused by:
            javax.persistence.TransactionRequiredException: no transaction is in progress
                at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413)
                at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3398)
                at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1355)
                at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1350)
                at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
                at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.base/java.lang.reflect.Method.invoke(Method.java:566)
                at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366)
                at com.sun.proxy.$Proxy95.flush(Unknown Source)

然后我尝试用 装饰 DAO @Transactional,并进一步用 装饰每个单独的方法@Transactional。还尝试使用 注入@PersistenceContext(unitName = "dbAUnit", type = PersistenceContextType.TRANSACTION),但所有这些尝试都会导致完全相同的错误。

为什么“二级”@PersistenceContext不进行交易?

标签: javaspring-bootjpaentitymanager

解决方案


当使用多个事务管理器时@Transactional,您需要明确声明要使用的事务管理器,否则@Primary管理器总是被选中,并且您的辅助EntityManager无法加入其事务。

尝试@Transactional("dbBTransactionManager")在注入第二个持久性上下文的任何地方使用,以查看问题是否消失。


推荐阅读