两个不同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;
    }
}

它们都用@PersistenceContext(unitName = "dbAUnit")@PersistenceContext(unitName = "dbBUnit")注入到DAO中,并且它们都成功地引导了内部的相应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)

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

“次要” @PersistenceContext为什么不进行交易?

解决方法

当多个事务管理器与@Transactional一起使用时,您需要声明该事务管理器以显式使用,否则@Primary管理器将始终被选中,并且辅助EntityManager可能无法加入交易。

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