web-dev-qa-db-fra.com

Spring Boot: Utiliser database et application.properties pour la configuration

J'ai besoin de sauvegarder la configuration de l'application Spring Boot dans la base de données.

Est-il possible de stocker les informations de la base de données dans le application.properties et de les utiliser pour se connecter à la base de données et extraire toutes les autres propriétés à partir de là?

Donc, mon application.properties ressemblerait à ceci:

spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=mydb
spring.datasource.username=user
spring.datasource.password=123456
spring.jpa.database-platform=org.hibernate.dialect.SQLServer2012Dialect

Et l'autre configuration serait extraite de la base de données avec quelque chose comme:

@Configuration
@PropertySource(value = {"classpath:application.properties"})
public class ConfigurationPropertySource {

    private final ConfigurationRepository configurationRepository;

    @Autowired
    public ConfigurationPropertySource(ConfigurationRepository configurationRepository) {
        this.configurationRepository = configurationRepository;
    }

    public String getValue(String key) {
        ApplicationConfiguration configuration = configurationRepository.findOne(key);
        return configuration.getValue();
    }

}

Avec ApplicationConfiguration comme Entity.

Mais Spring Boot n’obtient pas la configuration de la base de données.

4
deve

Une solution possible que vous pourriez utiliser consiste à utiliser ConfigurableEnvironment et à recharger et ajouter des propriétés.

@Configuration   
public class ConfigurationPropertySource {

private ConfigurableEnvironment env;

private final ConfigurationRepository configurationRepository;

    @Autowired
    public ConfigurationPropertySource(ConfigurationRepository configurationRepository) {
        this.configurationRepository = configurationRepository;
    }

    @Autowired
    public void setConfigurableEnvironment(ConfigurableEnvironment env) {

        this.env = env;
   }

   @PostConstruct
   public void init() {
    MutablePropertySources propertySources = env.getPropertySources();
       Map myMap = new HashMap();
       //from configurationRepository get values and fill mapp
       propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
   }

}
3
kuhajeyan

Malheureusement, je n'ai pas encore de solution à ce problème, mais j'utilise la solution suivante pour le moment (nécessite un redémarrage supplémentaire de l'application lors de la modification de la configuration).

@Component
public class ApplicationConfiguration {

    @Autowired
    private ConfigurationRepository configurationRepository;

    @Autowired
    private ResourceLoader resourceLoader;

    @PostConstruct
    protected void initialize() {
        updateConfiguration();
    }

    private void updateConfiguration() {
        Properties properties = new Properties();

        List<Configuration> configurations = configurationRepository.findAll();
        configurations.forEach((configuration) -> {
            properties.setProperty(configuration.getKey(), configuration.getValue());
        });

        Resource propertiesResource = resourceLoader.getResource("classpath:configuration.properties");
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(propertiesResource.getFile()))) {
            properties.store(out, null);
        } catch (IOException | ClassCastException | NullPointerException ex) {
            // Handle error
        }
    }

}

Je charge la configuration à partir de la base de données et l'écris dans un autre fichier de propriétés. Ce fichier peut être utilisé avec @PropertySource("classpath:configuration.properties").

2
deve

Une autre option consiste à utiliser ApplicationContextInitializer, avec l'avantage de pouvoir utiliser @Value directement et de pouvoir également contracter la priorité des propriétés.

import Java.sql.PreparedStatement;
import Java.sql.ResultSet;
import Java.util.HashMap;
import Java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;

public class ReadDBPropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    private static final Logger LOG = LoggerFactory.getLogger(ReadDBPropertiesInitializer.class);

    /**
     * Name of the custom property source added by this post processor class
     */
    private static final String PROPERTY_SOURCE_NAME = "databaseProperties";

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {

        ConfigurableEnvironment configEnv = ((ConfigurableEnvironment) applicationContext.getEnvironment());

        LOG.info("Load properties from database");

        Map<String, Object> propertySource = new HashMap<>();

        try {

            final String url = getEnv(configEnv, "spring.datasource.url");

            String driverClassName = getProperty(configEnv, "spring.datasource.driver-class-name");

            final String username = getEnv(configEnv, "spring.datasource.username");
            final String password = getEnv(configEnv, "spring.datasource.password");

            DataSource ds = DataSourceBuilder.create().url(url).username(username).password(password)
                    .driverClassName(driverClassName).build();

            // Fetch all properties
            PreparedStatement preparedStatement = ds.getConnection()
                    .prepareStatement("SELECT config_key as name, config_value as value, config_label as label FROM TB_CONFIGURATION");

            ResultSet rs = preparedStatement.executeQuery();

            // Populate all properties into the property source
            while (rs.next()) {             
                final String propName = rs.getString("name");
                final String propValue = rs.getString("value");
                final String propLabel = rs.getString("label");
                LOG.info(String.format("Property: %s | Label: %s", propName, propLabel));
                LOG.info(String.format("Value: %s", propValue));
                propertySource.put(propName, propValue);
            }

            // Create a custom property source with the highest precedence and add it to
            // Spring Environment
            applicationContext.getEnvironment().getPropertySources()
                    .addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));

        } catch (Exception e) {
            throw new RuntimeException("Error fetching properties from db");
        }

    }

    private String getEnv(ConfigurableEnvironment configEnv, final String property) {
        MutablePropertySources propertySources = configEnv.getPropertySources();
        PropertySource<?> appConfigProp = propertySources.get("applicationConfigurationProperties");
        return System.getenv().get(((String) appConfigProp.getProperty(property)).replace("${", "").replace("}", ""));
    }

    private String getProperty(ConfigurableEnvironment configEnv, final String property) {
        MutablePropertySources propertySources = configEnv.getPropertySources();
        PropertySource<?> appConfigProp = propertySources.get("applicationConfigurationProperties");
        return (String) appConfigProp.getProperty(property);
    }

Références:

  1. https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-customize-the-environment-or-application-context
  2. https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config

PS: Ce code est un mélange d’autres que j’ai trouvés sur Internet. Les crédits sont entièrement de leurs auteurs. Désolé de ne pas pouvoir trouver leurs liens, il y a eu de nombreux tests jusqu'à ce que vous les fassiez fonctionner. Mais si vous trouvez que cela ressemble à un autre trouvé là-bas, vous pouvez être sûr que c'est juste une dérivation. ;)

Environnement:

  1. Java: environnement d'exécution OpenJDK (build 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11)
  2. Printemps: 4.3.11
  3. Botte de printemps: 1.5.7
  4. Hibernate Core: 5.2.10-Final
1
samarone