3

Condition evaluation depends on a value provided in data base table

@Component
public class XYZCondition implements Condition{

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
              //based on value defined in DB should return true/false
    }

}

As Condition is executing very early, unable to fetch db value is there any alternate way to achieve this ?

Trushit Shekhda
  • 563
  • 7
  • 18
csr
  • 63
  • 1
  • 4
  • @levant pied : I have seen similar question do you have any findings – csr May 01 '20 at 05:38
  • It seems you're using Condition for not the same thing that they're intended to be used. Could you please describe your case in detail so that I can recommend you smth useful? – Mikhail Kopylov May 01 '20 at 06:00
  • Interface has multiple implementations and table has enable or disable value for each implemented class to decide the initialize the bean, so thinking of using @Condition to achieve this ? – csr May 01 '20 at 06:21
  • @user1702759 so how are you going to inject those enabled "multiple implementations" of your interface? – Ryan Guamos May 01 '20 at 06:32

5 Answers5

1

Database values can be changed during application work, while it doesn't seem a good idea to reload application context. So I would recommended using configuration properties to choose which beans should be available in context.

Moreover, there's a Spring Cloud Config that allows you to store configuration in git or some other storages. Its consumers may restart context once configuration changes. It seems worth talking a look at it as well.

Mikhail Kopylov
  • 2,008
  • 5
  • 27
  • 58
1

Well, you're trying to do something that doesn't map well to spring boot in this form.

I suggest to slightly change the requirement:

Instead of trying to access the database in the custom condition, create a custom source of configuration and load the property from the database into the Environment so that when the conditionals get evaluated later on during the startup process, the property with an associated value (previously resolved from the database) is already available.

Examples of following such an approach are: - Spring boot cloud config that reads the configuration properties from "remote" config service (via REST) - Spring boot consul integration (that reads from consul obviously)

This approach is much more spring-friendly, and also has can save the application from calling the database multiple times (what if you have 100 beans with this custom conditional) - will it do 100 queries?

Now, this will mean probably that you won't need a custom conditional - probably it will be @Condition on property.

Another caveat is that you won't be able to use JPA/Spring Data to load this property, probably you'll have to go with a Plain JDBC here.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
0

Hmm, maybe you can just create a Configuration Class and Inject your Repository then create a Bean. Then inside the bean method fetch the value from the database and return conditional instance. something like this

@Configuration
public class Config {

@Autowired  
private Repository repository;

@Bean
public Interface interface(){
  boolean val = reposiory.getDBValue();
  if(val)
    return new Impl1();
  else
    return new Impl2();
  }
}
Ryan Guamos
  • 240
  • 4
  • 14
0

sadly you cannot inject a lot into condition: so try with plain url and make manual connection to server, but in my case this didn't work as I had flyway migration that was adding the same configuration value into the database (chicken and egg problem)

class EnableXXXCondition implements Condition {
    private Environment environment;

    @SneakyThrows
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        environment = context.getBeanFactory().getBean(Environment.class);

        final String url = environment.getProperty("spring.datasource.url");
        final String user = environment.getProperty("spring.datasource.username");
        final String password = environment.getProperty("spring.datasource.password");

        String x;

        //we have problems with flyway before
        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            try (Statement statement = connection.createStatement()) {
                try (final ResultSet resultSet = statement.executeQuery("select x from y")) {
                    resultSet.next();
                    x = resultSet.getString("x");
                }
            }
        }

        return Boolean.valueOf(x);
    }
}
user3423
  • 41
  • 4
0

You can try defining your condition in your configuration file and use it like below :

@ConditionalOnProperty(name="filter.enabled", havingValue="true")
Elikill58
  • 4,050
  • 24
  • 23
  • 45