简体   繁体   中英

How to create datasource bean at runtime without crashing app in Spring?

I am connecting to multiple datasources but sometimes some datasources may be offline and at that time I am geting errors on app and application is failing at startup.

I want to skip datasource configuration at startup ... I have tried several ways by adding

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 

to the application.properties and also I have tried adding

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})

to the main class but still it tries to configure the datasource.

I also tried to use @Lazy annotation on all methods and on constructor as below but still getting error while creating fooEntityManagerFactory

@Lazy
@Configuration
@EnableJpaRepositories(basePackages = "com.heyo.tayo.repository.foo", entityManagerFactoryRef = "fooEntityManagerFactory", transactionManagerRef = "fooTransactionManager")
public class PersistencefooConfiguration {

    @Autowired
    private DbContextHolder dbContextHolder;

    @Lazy
    @Bean
    @ConfigurationProperties("tay.datasource.foo")
    public DataSourceProperties fooDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Lazy
    @Bean
    @ConfigurationProperties("tay.datasource.foo.configuration")
    public DataSource fooDataSource() {
        DataSource dataSource = fooDataSourceProperties().initializeDataSourceBuilder()
                    .type(BasicDataSource.class).build();
        dbContextHolder.addNewAvailableDbType(DbTypeEnum.foo);

        return dataSource;
    }

    @Lazy
    @Bean(name = "fooEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean fooEntityManagerFactory(
            EntityManagerFactoryBuilder builder) {

//THE CODE IS FAILING AT BELOW RETURN CASE

        return builder
                .dataSource(fooDataSource())
                .packages("com.heyo.tayo.model.foo")
                .build();
    }

    @Lazy
    @Bean
    public PlatformTransactionManager fooTransactionManager(
            final @Qualifier("fooEntityManagerFactory") LocalContainerEntityManagerFactoryBean fooEntityManagerFactory) {
        return new JpaTransactionManager(fooEntityManagerFactory.getObject());
    }

}

I have multiple classes like above for different configs for different datasources and I am adding them to available dbs static list at datasource Bean.

Here is my dbadapter factory class.

Here is my dbAdaptor factory that creates corresponding db adaptor

@Service
public class DbAdapterFactory {

    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private DbContextHolder dbContextHolder;

    public DBAdapter dbAdapter(){

        DbTypeEnum currentDb = dbContextHolder.getCurrentDb();
        DBAdapter dbAdapter = null;

        if(currentDb == DbTypeEnum.FOODB) {
            dbAdapter = beanFactory.getBean(foodbadaptor.class);
        } else {
            dbAdapter = beanFactory.getBean(koodbadaptor.class);
        }

        return dbAdapter;
    }

Here is db context holder that makes operation like setting default db or getting current db etc.:

@Component
public class DbContextHolder {

    private DbTypeEnum dbType = DbTypeEnum.FOODB;
    private Set<DbTypeEnum> availableDbTypes = new HashSet<>();

    public void setCurrentDb(DbTypeEnum dbType) {
        this.dbType = dbType;
    }

    public DbTypeEnum getCurrentDb() {
        return this.dbType;
    }

    public List<DbTypeEnum> getAvailableDbTypes() {
        return new ArrayList<>(availableDbTypes);
    }

    public void addNewAvailableDbType(DbTypeEnum dbTypeEnum) {
        availableDbTypes.add(dbTypeEnum);
    }
}

I made all @Lazy or tried @SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) but still something is calling to create bean and getting error and app is closing. I want to use that config and datasource in a try-catch block and don't stop application at runtime. How can I achieve this or what am I missing on that configs or annotations?

I believe that you can simply add in your application properties

spring.sql.init.continue-on-error=true

According to the Spring Boot 2.5.5 user guide:

https://docs.spring.io/spring-boot/docs/2.5.5/reference/htmlsingle/#howto-initialize-a-database-using-spring-jdbc

Spring Boot enables the fail-fast feature of its script-based database initializer. If the scripts cause exceptions, the application fails to start. You can tune that behavior by setting spring.sql.init.continue-on-error.

Depending on your spring boot version the property will be named either

spring.sql.init.continue-on-error

or before Spring Boot 2.5

spring.datasource.continue-on-error

It is so dumb but I solved the problem by adding following to application.properties.

spring.jpa.database=sql_server

I have no idea why I need to specify that explicitly in properties file but the problem is solved. I will search for it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM