【问题标题】:SpringBoot Multiple Data Source JPASpring Boot 多数据源 JPA
【发布时间】:2017-03-29 07:11:41
【问题描述】:

我有以下代码

实体

 @Entity
    public class Employee {

        @Id
        @GeneratedValue
        long id;

        @Column(name="first_name")
        String firstName;

        @Column(name="last_name")
        String lastName;

        @Column(name="salary")
        int salary;

        @ManyToOne
        @JoinColumn(name="address")
        Address address;

    ..... setter & getter

    }

回购

public interface EmpRepository extends JpaRepository<Employee, Long>{}

服务

@Service
public class EmpService {

    @Autowired
    private EmpRepository empRepo;

    @Autowired @Qualifier("primaryEntityManagerFactory") EntityManager em;

    public List<Employee> findAll(){
        return empRepo.findAll();
    }

}

控制器

@RestController
@RequestMapping(value = "/demo")
public class DemoController {

    @Autowired
    private EmpService empService;


    @RequestMapping("/abcd")
    public List<Employee> findAll(){
        return empService.findAll();
    }

}

数据源配置(主要)

@Configuration
@EnableJpaRepositories(basePackages="com.example",
entityManagerFactoryRef = "primaryEntityManagerFactory")
public class DataSourceConfiguration {

    @Bean(name="primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix = "datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(primaryEntityManagerFactory().getObject());
        return tm;
    }

    @Bean(name="primaryEntityManagerFactory")
    @Primary
    LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {

        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(primaryDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan(DataSourceConfiguration.class.getPackage().getName());

        return factoryBean;
    }

}

辅助数据源配置

 @Configuration
@EnableJpaRepositories(basePackages="com.example",
entityManagerFactoryRef = "secondaryEntityManagerFactory")
public class DS2Configuration {

    @Bean(name="secondaryDataSource")
    @ConfigurationProperties(prefix = "datasource.secondary")

    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(secondaryEntityManagerFactory().getObject());
        return tm;
    }

    @Bean(name="secondaryEntityManagerFactory")
    LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory() {

        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(secondaryDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan(DataSourceConfiguration.class.getPackage().getName());

        return factoryBean;
    }

}

application.properties

spring.jpa.show-sql = true
spring.jpa.properties.hibernate.show_sql=true
spring.jooq.sql-dialect=MYSQL
logging.level.org.springframework.data=DEBUG

# Primary DataSource configuration
datasource.primary.url=jdbc:mysql://127.0.0.1:3306/jpa
datasource.primary.username=root
datasource.primary.password=root

# Secondary DataSource configuration
datasource.secondary.url=jdbc:mysql://127.0.0.1:3306/jpa2
datasource.secondary.username=root
datasource.secondary.password=root

# Disable Spring DataSource auto-initialization
spring.datasource.initialize=false

server.port=8081

当我从 EmpService 实际运行函数 findAll() 时,即使我将辅助 entitymanagefactory 指定为,它也始终使用主数据源

@Autowired
    @Qualifier("secondaryEntityManagerFactory")
    EntityManager em;

如何解决这个问题? 附言- 请不要为此分享博客链接。

【问题讨论】:

    标签: jpa spring-boot spring-data-jpa


    【解决方案1】:

    您需要将存储库类放在两个不同的目录中。并改变

    @EnableJpaRepositories(basePackages="com.example".....)  
    

    在您配置数据源的两个地方都行。这应该可以解决您的
    代码运行但始终使用主数据源的问题

    问候

    【讨论】:

    • 是的,它奏效了。但是不能给两个配置文件相同的basePackage并在运行时指定配置吗?
    • 我不确定,因为@componentscan 基本上适用于包。但是查看文档(docs.spring.io/spring-data/data-jpa/docs/1.7.0.M1/api/org/…),您可以尝试使用 excludeFilters。我没有亲自尝试过。让我知道它是否有效:)
    【解决方案2】:

    发生这种情况是因为有多个 javax.persistence.EntityManagerFactory 类型的 bean 来帮助 spring boot 决定更喜欢哪个 bean

    使用 @primary 注释来注释 bean

    @primary official documentation

    【讨论】:

    • 它抱怨的是非唯一的 entityManagerFactories 而不是数据源。他的数据源设置看起来不错。
    【解决方案3】:

    你需要在这个方法上添加@Primary:primaryEntityManagerFactory()

    顺便说一句,我认为primaryTransactionManager()和等效的辅助方法是多余的,工厂bean方法返回相同的东西。

    您还需要为每个 entityManager 定义一个 transactionManager,例如

    @Bean
    @Primary
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(yourPrimaryEntityManagerInjected);
        return tm;
    }
    

    【讨论】:

    • 您需要为每个实体管理器定义一个事务管理器bean。我会把它添加到我的回复中。
    • 完成了。 @Bean(name="primaryTransactionManager") @Primary public JpaTransactionManager primaryTransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(primaryEntityManagerFactory().getNativeEntityManagerFactory());返回 tm; } 但仍然是同样的错误
    • 此外,当我从两个配置文件中删除 transactionManager 定义时,代码运行但始终使用主数据源,即使我使用 @Autowired @Qualifier("secondaryEntityManagerFactory") EntityManager em 在服务类上指定了次要数据源;
    • 打开 org.springframework 的调试,看看自动装配是如何发生的。就事务管理器而言,有一个单独的 java 配置,而不是执行此 primaryEntityManagerFactory().get‌​NativeEntityManagerF‌​actory() 只需使用注入工厂,即此 primaryEntityManagerFactory。祝你好运,调试日志应该可以帮助你弄清楚发生了什么。
    • 现在不会出现错误。我在两个配置中都添加了 transactionManager。但问题在于每次使用主要数据源。我现在错过了什么?