Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot: Retrieving configuration from a database

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.

like image 292
mip Avatar asked May 13 '15 10:05

mip


1 Answers

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
like image 181
Federico Piazza Avatar answered Oct 18 '22 03:10

Federico Piazza