Spring Boot: Retrieving configuration from a database

12,139

I have used the EnvironmentPostProcessor spring feature to do this.

You need to create a class like this:

public class ReadDbPropertiesPostProcessor implements EnvironmentPostProcessor {
    /**
     * Name of the custom property source added by this post processor class
     */
    private static final String PROPERTY_SOURCE_NAME = "databaseProperties";
    
    /**
     * Adds Spring Environment custom logic. This custom logic fetch properties from database and setting highest precedence
     */
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Map<String, Object> propertySource = new HashMap<>();

        try {
            // Build manually datasource to ServiceConfig
            DataSource ds = DataSourceBuilder
                    .create()
                    .username(USERNAME) // replace with your config
                    .password(PASSWORD) // replace with your config
                    .url(DATASOURCE-URL)// replace with your config
                    .driverClassName(DRIVER) // replace with your config
                    .build();

            // Fetch all properties
            PreparedStatement preparedStatement = ds.getConnection().prepareStatement("SELECT name, value FROM propertyConfig WHERE service = ?");
            preparedStatement.setString(1, APP_NAME);
            
            ResultSet rs = preparedStatement.executeQuery();

            // Populate all properties into the property source
            while (rs.next()) {
                String propName = rs.getString("name");
                propertySource.put(propName, rs.getString("value"));
            }
            
            // Create a custom property source with the highest precedence and add it to Spring Environment 
            environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));
        
        } catch (Exception e) {
            throw new RuntimeException("Error fetching properties from db");
        }
    }
}

Since you need to run this class at a very early stage of spring, you need to create the file spring.factories and register your environment post processor. This file needs to be located here:

src/main/resources/META-INF/spring.factories

In the content you need to set your class to the spring property:

# Environment Post Processor
org.springframework.boot.env.EnvironmentPostProcessor=com.your.package.ReadDbPropertiesPostProcessor
Share:
12,139

Related videos on Youtube

mip
Author by

mip

Updated on June 15, 2022

Comments

  • mip
    mip almost 2 years

    Can anyone offer me some guidance on the best way for me to achieve this goal.

    I'd like to extend the Spring Boot Externalized Configuration so that I have a single method which can be called from anywhere in my application. This method will retrieve a property value using a key. This method will first interrogate a database table and if it does not find the specified key it will then fall back on the PropertySource order described in 1.

    So I'd have a service similar to:

    @Service
    public class ConfigurationService {
    
        private final ConfigurationRepository configurationRepository;
    
        @Autowired
        public ConfigurationService(ConfigurationRepository configurationRepository) {
            this.configurationRepository = configurationRepository;
        }
    
        public String getValue(String key) {
            Configuration configuration = configurationRepository.findOne(key);
    
            // Add something here to get the property from application.properties if the key does not exist in the db
    
            return configuration == null ? null : configuration.getValue();
        }
    
    }
    

    Which I can use as follows:

    foo = configuration.getValue("my.property");
    

    Is there a better way of going about this? Am I missing a Spring Boot feature that I could utilise?

    EDIT: I'd like to be able to change the values of the properties while the application is running and have these new values picked up.

    • chrylis -cautiouslyoptimistic-
      chrylis -cautiouslyoptimistic- almost 9 years
      Depending on how large your project/deployment is, this sounds like a potential case for Spring Cloud Config.
    • M. Deinum
      M. Deinum almost 9 years
      Write a PropertySource that is backed by the database, that way it just integrates with the rest of the system. Or simply write an ApplicationInitializer which loads all the properties from a database, wrap them in a PropertiesPropertySource and add them to the environment. At least you don't want to go calling this method around the whole place you want to integrate with the default mechanisms.