将spring-boot-starter-web添加到依赖项会破坏多个数据源

时间:2015-10-16 20:19:14

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

我有一个包含3个不同DataSource的项目。如果项目是使用spring-boot运行的话,它可以正常工作:只运行这些依赖项:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.6.RELEASE</version>
</parent>
<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>RELEASE</version>
    </dependency>

    <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-web</artifactId>-->
    <!--</dependency>-->

这是一个数据源,它们几乎完全相同,只是更改bean名称和数据库信息

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "emfIntranet", transactionManagerRef = "tmIntranet", basePackages = {"com.vnt.intranet.repositories"})
@ConfigurationProperties(prefix = "databases.sistemas")
public class IntranetPersistence {

    private String address;
    private String schema;
    private String username;
    private String password;
    private String eclipselinklog;
    private Boolean sqllog;

    @Primary
    @Bean(name = "dsIntranet")
    public DataSource dataSource() {
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setInitialSize(3);
        dataSource.setMaxIdle(10);
        dataSource.setMaxActive(10);
        return dataSource;
    }

    private EclipseLinkJpaVendorAdapter getEclipseLinkJpaVendorAdapter() {
        EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
        vendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.PostgreSQLPlatform");
        vendorAdapter.setShowSql(sqllog);
        return vendorAdapter;
    }

    @Primary
    @Bean(name = "emfIntranet")
    public EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setJpaVendorAdapter(getEclipseLinkJpaVendorAdapter());
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan("com.vnt.intranet.entities");
        factoryBean.setPersistenceUnitName("intranet");

        Properties jpaProperties = new Properties();
        jpaProperties.put("eclipselink.weaving", "false");
        jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE / FINEST

        factoryBean.setJpaProperties(jpaProperties);
        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }

    @Primary
    @Bean(name = "tmIntranet")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory());
        return transactionManager;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEclipselinklog() {
        return eclipselinklog;
    }

    public void setEclipselinklog(String eclipselinklog) {
        this.eclipselinklog = eclipselinklog;
    }

    public Boolean getSqllog() {
        return sqllog;
    }

    public void setSqllog(Boolean sqllog) {
        this.sqllog = sqllog;
    }
}

我可以毫无问题地访问所有数据源......其中一个用@Primary注释。

但是,如果我取消注释spring-boot-starter-web依赖关系,它会破坏它并给我:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: more than one 'primary' bean found among candidates: [emfIntranet, entityManagerFactory, emfMkRadius, emfMkData]

我正在尝试将此转换为一个没有成功的网络项目......

有什么想法吗?

修改 为清晰起见,添加其他类:

MkDataPersistence.class

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "emfMkData", transactionManagerRef = "tmMkData", basePackages = {"org.example.mkdata.repositories"})
@ConfigurationProperties(prefix = "databases.mkdata")
public class MkDataPersistence {

    private String address;
    private String schema;
    private String username;
    private String password;
    private String eclipselinklog;
    private Boolean sqllog;

    @Bean(name = "dsMkData")
    javax.sql.DataSource dataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setInitialSize(3);
        dataSource.setMaxIdle(10);
        dataSource.setMaxActive(10);
        return dataSource;
    }

    @Bean
    HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL9Dialect");
        vendorAdapter.setShowSql(sqllog);
        return vendorAdapter;
    }

    @Bean(name = "emfMkData")
    EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan("org.example.mkdata.entities");
        factoryBean.setPersistenceUnitName("mkdata");

        Properties jpaProperties = new Properties();
        jpaProperties.put("eclipselink.weaving", "false");
        jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE / FINEST

        factoryBean.setJpaProperties(jpaProperties);
        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }

    @Bean(name = "tmMkData")
    PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory());
        return transactionManager;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEclipselinklog() {
        return eclipselinklog;
    }

    public void setEclipselinklog(String eclipselinklog) {
        this.eclipselinklog = eclipselinklog;
    }

    public Boolean getSqllog() {
        return sqllog;
    }

    public void setSqllog(Boolean sqllog) {
        this.sqllog = sqllog;
    }
}

MkRadiusPersistence.class

@Configuration
@EnableTransactionManagement()
@EnableJpaRepositories(entityManagerFactoryRef = "emfMkRadius", transactionManagerRef = "tmMkRadius", basePackages = {"org.example.mkradius.repositories"})
@ConfigurationProperties(prefix = "databases.mkradius")
public class MkRadiusPersistence {

    private String address;
    private String schema;
    private String username;
    private String password;
    private String eclipselinklog;
    private Boolean sqllog;

    @Bean(name = "dsMkRadius")
    javax.sql.DataSource dataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setInitialSize(3);
        dataSource.setMaxIdle(10);
        dataSource.setMaxActive(10);
        return dataSource;
    }

    @Bean
    HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL9Dialect");
        vendorAdapter.setShowSql(sqllog);
        return vendorAdapter;
    }

    @Bean(name = "emfMkRadius")
    EntityManagerFactory entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan("org.example.mkradius.entities");
        factoryBean.setPersistenceUnitName("mkradius");

        Properties jpaProperties = new Properties();
        jpaProperties.put("eclipselink.weaving", "false");
        jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE / FINEST

        factoryBean.setJpaProperties(jpaProperties);
        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }

    @Bean(name = "tmMkRadius")
    PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory());
        return transactionManager;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEclipselinklog() {
        return eclipselinklog;
    }

    public void setEclipselinklog(String eclipselinklog) {
        this.eclipselinklog = eclipselinklog;
    }

    public Boolean getSqllog() {
        return sqllog;
    }

    public void setSqllog(Boolean sqllog) {
        this.sqllog = sqllog;
    }
}

编辑2

Application.class

@Configuration
@ComponentScan(basePackages = { "org.example.startup" })
@EnableAutoConfiguration
public class Application {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    CableRouteRepository cableRouteRepository;

    @Autowired
    CityRepository cityRepository;

    @Autowired
    RadAcctRepository radAcctRepository;

    public static void main(String[] args) {

        ConfigurableApplicationContext context = new SpringApplicationBuilder()
                .showBanner(false)
                .sources(Application.class)
                .run(args);

        Application app = context.getBean(Application.class);

//        for (String bean: context.getBeanDefinitionNames()) {
//            logger.info(bean);
//        }

        app.start();
    }

    private void start() {
        logger.info("Application.start()");

        logger.info("{}", cableRouteRepository.findAll());
        logger.info("{}", cityRepository.findAll());
        logger.info("{}", radAcctRepository.findTest());
    }


}

这是入门类...​​我将每个存储库打印为测试(这里的每个存储库都在不同的DataSource上)......只要我在类路径上没有spring-starter-web,它们就可以正常工作

编辑3

Github Repo https://github.com/mtrojahn/test-multiple-databases

我希望我做得对...我从未真正与Github合作过:)

编辑4

Github使用失败的代码正确更新。

作为提醒,如果注释依赖项,则代码可以正常工作:

<dependency>
    <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

如果上面的依赖项没有注释,但是下面的代码在IntranetPersistence.class中改变了:

@Primary
@Bean(name = "emfIntranet")

@Primary
@Bean(name = "entityManagerFactory")

它会覆盖默认bean并开始失败:

Caused by: java.lang.IllegalArgumentException: Not an managed type: class org.example.intranet.entities.CableRoute

1 个答案:

答案 0 :(得分:1)

您受到Spring Boot 1.2的JPA自动配置行为的影响。如果有用户定义的entityManagerFactory,它只会关闭自己的LocalContainerEntityManagerFactoryBean bean的创建。您正在使用LocalContainerEntityManagerFactoryBean,但请自行调用afterPropertiesSetgetObject,而不是允许容器为您执行此操作。这使得上下文将包含多个@Primary EntityManagerFactory个bean。这是improved in Spring Boot 1.3,因此用户声明的EntityManagerFactory bean也会关闭自动配置。

这在尝试创建openEntityManagerInViewInterceptor时会导致问题,因为它需要EntityManagerFactory并且上下文无法知道它应该选择的两个@Primary bean的哪个。

有几种方法可以继续。您可以更新配置以定义类型为LocalContainerEntityManagerFactoryBean而不是EntityManagerFactory的bean。另一种方法是通过将以下内容添加到application.yml

来禁用拦截器的创建
spring:
  jpa:
    open_in_view: false