Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot: Conditional on database type

I have a Spring Boot application and want to inject specific repository implementations based on the type of the underlying SQL data source (defined using the spring.datasource.* properties).

I tried to use a custom conditional here, but unfortunately, when it is evaluated I cannot get the data source in order to check for the database type.

My condition currently looks like this:

@Order(Ordered.LOWEST_PRECEDENCE - 10)
class OnDatabaseTypeCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        DataSource dataSource = context.getBeanFactory().getBean(DataSource.class);
        // further matching code here

}

It should be used something like this:

@ConditionalOnDatabaseType(H2)
public class MyCustomImplementation implements MyRepository {
    // Code
}

However, when the condition is evaluated, I get an exception that the data source is not defined:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:348)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)

I assume that this is happening because the condition is already checked before all the beans are constructed. Is there any way that I can tell Spring that the data source has to be created before this condition is evaluated? Or is this just no possible using Spring conditions?

The use case I want to solve by doing is the following: The application can run with different types of databases, and some (like H2) do not certain functions like windowing functions. So I want to provide special queries for certain databases that may be slower, but still functionally correct. The idea for this approach was that these JDBC repositories should be easy to mark without the necessity of introducing specific Spring profiles, but just by looking at the underlying database (using org.springframework.jdbc.support.JdbcUtils).

Thanks

like image 770
Gernot R. Bauer Avatar asked Oct 29 '22 18:10

Gernot R. Bauer


1 Answers

Some conditions are evaluated when the bean is actually created. If you want to check for the presence of a bean, you need to be very careful because any attempt to get a bean at that stage will lead to an early initialization. That's the first problem.

The second problem is that your configuration (app configuration) always run completely before the auto-configuration. If Spring Boot has to decide if it must create a DataSource, it should give the app configuration a chance to provide one.

The only way you could implement that is via auto-configuration, making sure that your auto-configuration runs after the ones that are supposed to provide the bean you're looking for ( @AutoconfigureAfter). Regardless I wouldn't do a straight call to the context like that. Check OnBeanCondition to see how Spring Boot does that thing itself. So you can't add such condition to your app configuration or to your components scanned by component scanning.

Having said that, the use case looks quite weird to me. Maybe we could take a step back and you could explain why you need to do this.

like image 107
Stephane Nicoll Avatar answered Nov 15 '22 05:11

Stephane Nicoll