Spring Boot自动从错误的数据源生成表

时间:2018-05-31 09:49:19

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

我当前的项目需要连接到多个数据库。我设置了

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
application.properties 中的

我有一些dbConfig如下:

@Configuration
public class DBSourceConfiguration {
    public final static String DATA_SOURCE_PRIMARY = "dataSource";
    public final static String DATA_SOURCE_PROPERTIES = "propertiesDataSource";
    public final static String DATA_SOURCE_REPORT = "reportDataSource";
    public final static String DATA_SOURCE_NEW_DRAGON = "newDragonDataSource";

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

    @Bean(name = DATA_SOURCE_REPORT)
    @ConfigurationProperties(prefix = "externaldatasource.report")
    public DataSource reportDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = DATA_SOURCE_NEW_DRAGON)
    @ConfigurationProperties(prefix = "externaldatasource.newdragon")
    public DataSource newDragonDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = DATA_SOURCE_PROPERTIES)
    @ConfigurationProperties(prefix = "externaldatasource.properties")
    public DataSource propertiesDataSource() {
        return DataSourceBuilder.create().build();
    }
}

<!-- language: Java -->

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef = PrimaryDbConfig.ENTITY_MANAGER_FACTORY, 
            transactionManagerRef = PrimaryDbConfig.TRANSACTION_MANAGER, 
            basePackageClasses = { _TbsRepositoryBasePackage.class })
    public class PrimaryDbConfig extends AbstractDbConfig {
        public final static String ENTITY_MANAGER_FACTORY = "entityManagerFactoryPrimary";
        public final static String ENTITY_MANAGER = "entityManagerPrimary";
        public final static String TRANSACTION_MANAGER = "transactionManagerPrimary";

        @Autowired
        @Qualifier(DBSourceConfiguration.DATA_SOURCE_PRIMARY)
        private DataSource dataSource;

        @Primary
        @Bean(name = ENTITY_MANAGER_FACTORY)
        public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
            return builder.dataSource(dataSource).properties(getVendorProperties(dataSource)).packages(_TbsEntityBasePackage.class).persistenceUnit("primaryPersistenceUnit").build();
        }

        @Primary
        @Bean(name = ENTITY_MANAGER)
        public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
            return entityManagerFactory(builder).getObject().createEntityManager();
        }

        @Primary
        @Bean(name = TRANSACTION_MANAGER)
        public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactory(builder).getObject());
        }    
    }

<!-- language: Java -->

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef = PropertiesDbConfig.ENTITY_MANAGER_FACTORY, 
            transactionManagerRef = PropertiesDbConfig.TRANSACTION_MANAGER, 
            basePackageClasses = { _PropertiesRepositoryBasePackage.class })
    public class PropertiesDbConfig extends AbstractDbConfig {
        public final static String ENTITY_MANAGER_FACTORY = "entityManagerFactoryProperties";
        public final static String ENTITY_MANAGER = "entityManagerProperties";
        public final static String TRANSACTION_MANAGER = "transactionManagerProperties";

        @Autowired
        @Qualifier(DBSourceConfiguration.DATA_SOURCE_PROPERTIES)
        private DataSource dataSource;

        @Bean(name = ENTITY_MANAGER_FACTORY)
        public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
            return builder.dataSource(dataSource).properties(getVendorProperties(dataSource)).packages(_PropertiesEntityBasePackage.class).persistenceUnit("propertiesPersistenceUnit").build();
        }

        @Bean(name = ENTITY_MANAGER)
        public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
            return entityManagerFactory(builder).getObject().createEntityManager();
        }

        @Bean(name = TRANSACTION_MANAGER)
        public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactory(builder).getObject());
        }
    }

以及另外两个DBConfig类(就像上面的两个DbConfig类一样)。

我的问题是每次运行此Web应用程序时,实体(在不同的包下)都会生成所有数据库。换句话说, Tbs(主要)实体将为 newDragon 和所有其他数据库生成表格。

例如,实体A属于主数据源,实体B属于属性数据。但框架会为主数据库 newDragon数据库以及其他两个数据库生成表A,B。

更新2018/06/01 - 1

尽管框架会为所有数据库生成所有实体,但我仍然可以从正确的数据库访问表。我的所有Web应用程序功能都运行良好。这很奇怪,不是吗?

我想我的配置很好,所以在我的应用程序访问数据库时没有任何问题(比如从错误的数据库中读取并获取空结果或将数据插入错误的数据库等)。可能还有别的东西会导致所有数据库出现这种问题。

1 个答案:

答案 0 :(得分:1)

根据您提供的配置,来自正确数据库的CRUD表不应该是问题。但是将表生成到正确的数据库中,有时您可能需要检查配置是否正确选择实体/包名称。

每个LocalContainerEntityManagerFactoryBean都使用唯一的包类设置,然后框架将扫描此包名下的实体并在目标数据源上相应地生成表;但是,有一种情况是packageToScan将被更改。正如你有@EntityScan注释,它会 覆盖所有已定义的LocalContainerEntityManagerFactoryBean上的packagesToScan,参考代码如下: EntityScanRegistrar.java

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof LocalContainerEntityManagerFactoryBean) {
        LocalContainerEntityManagerFactoryBean factoryBean = (LocalContainerEntityManagerFactoryBean) bean;
        factoryBean.setPackagesToScan(this.packagesToScan);
        this.processed = true;
    }
    return bean;
}

因此,即使您为每个LocalContainerEntityManagerFactoryBean提供了唯一的包类,如果您在应用程序的某个地方使用@EntityScan,最终结果仍可能被框架覆盖。您的配置对我来说似乎没问题,因此首先尝试查找并解析@EntityScan和LocalContainerEntityManagerFactoryBean之间的软件包名称,它应该可以解决问题。

参考:https://github.com/spring-projects/spring-boot/issues/6830