@PersistenceContext on two different LocalContainerEntityManagerFactoryBean throws TransactionRequiredException

huangapple go评论70阅读模式
英文:

@PersistenceContext on two different LocalContainerEntityManagerFactoryBean throws 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() {
        // ...
    }

    @Bean(name = "dbATransactionManager")
    @Primary
    public PlatformTransactionManager dbATransactionManager() {
        // ...
    }

    Properties additionalProperties() {
        // ...
    }
}

这两者都在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)
    // ...

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

为什么“次要”的@PersistenceContext没有生效事务?

英文:

I have two LocalContainerEntityManagerFactoryBeans set up, one with @Primary annotation and one without. This is because I have to communicate with two distinct databases, and if I remove the @Primary decoration I get a duplicate bean error. Both of them are constructed in the exact same way, here's one for reference:

package com.myorg.rest.config.dba;

import ...

@Configuration
@EnableJpaRepositories(
    basePackages = &quot;com.myorg.rest.dao.dbA&quot;,
    entityManagerFactoryRef = &quot;dbAEntityManager&quot;,
    transactionManagerRef = &quot;dbATransactionManager&quot;
    )
@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 = &quot;dbAEntityManager&quot;)
    @Primary
    public LocalContainerEntityManagerFactoryBean dbAEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(prjDataSource());
        em.setPackagesToScan(new String[] { &quot;com.myorg.model.entities.dba&quot; });
        em.setPersistenceUnitName(&quot;dbAUnit&quot;);

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

        return em;
    }

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

        return transactionManager;
    }

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

Both of them are injected inside DAOs with @PersistenceContext(unitName = &quot;dbAUnit&quot;) and @PersistenceContext(unitName = &quot;dbBUnit&quot;), and they are both successfully bootstrapping the corresponding EntityManager inside. I can create in both of them and then retrieve the created entity. But when I go to retrieve all entities with the following:

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

One of them works flawlessly and the other one doesn't (it returns 0 entities). After debugging, I've realized that one of them was issuing the insert into... sql command, while the other one wasn't. I tried to force with entityManager.flush() and got

 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)

I then tried to decorate the DAOs with @Transactional, and further decorate each individual method with @Transactional. Also tried injecting with @PersistenceContext(unitName = &quot;dbAUnit&quot;, type = PersistenceContextType.TRANSACTION), but all of those attempts result in the exact same error.

Why is the 'secondary' @PersistenceContext not putting the transactions in?

答案1

得分: 1

当在使用多个事务管理器与@Transactional时,您需要明确声明要使用的事务管理器,否则始终会选择@Primary管理器,而您的次要EntityManager可能无法加入其事务。

尝试在注入第二个持久化上下文的地方使用@Transactional(&quot;dbBTransactionManager&quot;),看看问题是否消失。

英文:

When using multiple transaction managers with @Transactional, you need to declare the transaction manager to use explicitly, otherwise the @Primary manager always gets selected, and your secondary EntityManager cannot possibly join its transaction.

Try using @Transactional(&quot;dbBTransactionManager&quot;) wherever the second persistence context is injected, to see if the problem goes away.

huangapple
  • 本文由 发表于 2020年9月28日 21:30:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/64103170.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定