如何在使用Spring时注入多个JPA EntityManager(持久化单元)

29

我需要使用一个数据库进行查询(非修改性操作),另一个数据库进行命令(修改操作)。我正在使用Spring Data JPA,因此我有两个配置类:

@Configuration
@EnableJpaRepositories(value = "com.company.read",
        entityManagerFactoryRef = "readingEntityManagerFactory",
        transactionManagerRef = "readingTransactionManager")
@EnableTransactionManagement
public class SpringDataJpaReadingConfiguration {

    @Bean(name = "readingEntityManagerFactory")
    public EntityManagerFactory readingEntityManagerFactory() {
        return Persistence.createEntityManagerFactory("persistence.reading");
    }

    @Bean(name = "readingExceptionTranslator")
    public HibernateExceptionTranslator readingHibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

    @Bean(name = "readingTransactionManager")
    public JpaTransactionManager readingTransactionManager() {
        return new JpaTransactionManager();
    }

}

@Configuration
@EnableJpaRepositories(value = "com.company.write",
        entityManagerFactoryRef = "writingEntityManagerFactory",
        transactionManagerRef = "writingTransactionManager")
@EnableTransactionManagement
public class SpringDataJpaWritingConfiguration {

    @Bean(name = "writingEntityManagerFactory")
    public EntityManagerFactory writingEntityManagerFactory() {
        return Persistence.createEntityManagerFactory("persistence.writing");
    }

    @Bean(name = "writingExceptionTranslator")
    public HibernateExceptionTranslator writingHibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

    @Bean(name = "writingTransactionManager")
    public JpaTransactionManager writingTransactionManager() {
        return new JpaTransactionManager();
    }

}

在我的代码库中,有时需要决定要使用哪个EntityManager,例如:

@Repository
public class UserReadingRepository {

    @PersistenceContext(unitName = "persistence.reading")
    private EntityManager em;

    // some useful queries here
}

我正在使用在我的 persistence.xml 文件中定义的持久化单元名称:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="persistence.reading" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <non-jta-data-source>ReadingDS</non-jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>

    <persistence-unit name="persistence.writing" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <non-jta-data-source>WritingDS</non-jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>

</persistence>

Spring抛出org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading'未定义。奇怪的是,它似乎尝试用持久化单元名称实例化一个bean? 我是否配置错误了?

更新: 当我从@PersistenceContext注释中删除unitName = "persistence.reading"时,我会得到以下错误: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: readingEntityManagerFactory,writingEntityManagerFactory

更新2: Rohit建议(在评论中)改为使用EntityManagerFactory。所以我尝试了以下操作:

@PersistenceUnit(unitName = "persistence.reading")
private EntityManagerFactory emf;

但是 Spring 只报告:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading' is defined

最终修复: 感谢 Vlad 的答案,我能够更新代码并使用以下内容(只需确保定义您的 dataSource bean):

@Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setPersistenceUnitName("persistence.reading");
    em.setDataSource(dataSource());
    em.setPackagesToScan("com.company");
    em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    em.afterPropertiesSet();
    return em.getObject();
}

为什么您没有设置transactionManagerentityManagerFactory属性?您的persistence.xml文件在哪里? - Rohit Jain
persistence.xmlMETA-INF 文件夹下,位于类路径中。我尝试在 transactionManager bean 上设置 entityManagerFactories,但结果完全相同。 - Xorty
我很确定 persistence.xml 是可见的,如果我回退到只使用一个 entityManager,那么 Spring 就可以成功地组装所有的 bean。 - Xorty
这很奇怪。虽然我遵循了基于Java的配置方法,在我的应用程序中也使用了多个entityManagers,但它完美地工作了,带有不同的持久性单元名称。如果对1起作用,那么没有理由不适用于2。或者可能是某个非常微小的东西丢失了……我对PUs的名称有一点疑问。这可能听起来很傻,但您能否在 persistence.readingpersistence.writing 中用 - 替换 .? - Rohit Jain
呵呵,我已经想到了,我改变了持久化单元的名称,但不幸的是没有运气 :-/ 不过我更新了原来的问题,加上了另一个有趣的错误。 - Xorty
显示剩余8条评论
1个回答

18

EntityManageFactory没有正确配置。你应该使用LocalContainerEntityManagerFactoryBean

@Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setPersistenceUnitName("persistence.reading");
    em.setDataSource(dataSource());
    em.setPackagesToScan("com.company");
    em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    em.afterPropertiesSet();
    return em.getObject();
}

同时JpaTransactionManager也被配置错误了,应该像以下这样:

@Bean(name = "readingTransactionManager")
public PlatformTransactionManager readingTransactionManager(){
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(readingEntityManagerFactory());
    return transactionManager;
}

你需要对读和写的EntityManager配置都做同样的操作。


1
谢谢Vlad,我终于成功了。请查看我在原问题中的更新,并修复您的答案(至少可以编译:)),以便其他人遇到此问题时也能够解决。 - Xorty
听明白了。我是用平板电脑写这个答案的,在编辑时在脑海中模拟编译器的工作。我已经按照您的最终修正更新了答案,以避免任何问题和混淆。 - Vlad Mihalcea
我也在尝试使用上述方法创建两个实体管理器工厂,但是在我的情况下,数据源相同,但仍然出现了相同的问题:没有找到类型为 'javax.persistence.EntityManagerFactory' 的合格bean:期望找到单个匹配的bean,但找到了2个。有任何指针吗? - user1575601

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接