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.
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));
}
}
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")
.
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:
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: