Spring Download: use the database and application.properties to configure

I need to save the configuration of the Spring Boot application in the database.

Is it possible to store information about the database in application.propertiesand use them to connect to the database and extract all other properties from it?

So mine application.propertieswill look like this:

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

And another configuration will be retrieved from the database like this:

@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();
    }

}

With ApplicationConfigurationhow Entity.

But Spring Boot does not get the configuration from the database.

+5
source share
5 answers

, , ConfigurableEnvironment .

@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

, , , ( ).

@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
        }
    }

}

. @PropertySource("classpath:configuration.properties").

+3

ApplicationContextInitializer @Value, .

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);
    }

:

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

:

  1. Java: OpenJDK ( 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-B11)
  2. Spring: 4.3.11
  3. Spring Boot: 1.5.7
  4. Hibernate Core: 5.2.10-Final
+2

, Spring Cloud Config: https://cloud.spring.io/spring-cloud-config/

ad git (= database) . .

, !

0

, , , , , .

- org.springframework.boot.env.EnvironmentPostProcessor. , . META-INF/spring.factories :

org.springframework.boot.env.EnvironmentPostProcessor=my.package.name.DBPropertiesLoaderEnvironmentPostProcessor

: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-boot-application.html#howto-customize-the-environment-or-application-context

package my.package.name;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;

import lombok.extern.slf4j.Slf4j;

/**
 * This is used to load property values from the database into the spring application environment
 * so that @Value annotated fields in the various beans can be populated with these database based
 * values.   I can also be used to store spring boot configuration parameters
 * 
 * In order for Spring to use this post porcessor this class needs to be added into the META-INF/spring.factories file like so:
 *  org.springframework.boot.env.EnvironmentPostProcessor=my.package.name.DBPropertiesLoaderEnvironmentPostProcessor
 * 
 * It will look for the spring boot dataseource properties that traditionally get stored in the application.yml files and use
 * those to create a connection to the database to load the properties.  It first looks for the datasource jndi name property
 * and if that fails it looks for the Spring.datasource.url based properties.
 * 
 *
 */
@Slf4j
public class DBPropertiesLoaderEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
        System.out.println("***********************************Pulling properties from the database***********************************");
        if(env.getProperty("spring.datasource.jndi-name") != null) {
            log.info("Extracting properties from the database using spring.datasource.jndi-name");
            try {
                JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
                dsLookup.setResourceRef(true);
                DataSource ds = dsLookup.getDataSource(env.getProperty("spring.datasource.jndi-name"));
                try(Connection con = ds.getConnection()) {
                    env.getPropertySources().addFirst(new DataBasePropertySource(con));
                }
                log.info("Configuration properties were loaded from the database via JNDI Lookup");
            } catch (DataSourceLookupFailureException | SQLException e) {
                log.error("Error creating properties from database with jndi lookup", e);
                e.printStackTrace();
            }
        } else if(env.getProperty("spring.datasource.url") != null){
            String url = env.getProperty("spring.datasource.url");
            String driverClass = env.getProperty("spring.datasource.driver-class-name");
            String username = env.getProperty("spring.datasource.username");
            String password = env.getProperty("spring.datasource.password");
            try {
                DriverManager.registerDriver((Driver) Class.forName(driverClass).newInstance());
                try(Connection c = DriverManager.getConnection(url,username,password);){
                    env.getPropertySources().addFirst(new DataBasePropertySource(c));
                    log.info("Configuration properties were loaded from the database via manual connection creation");
                }
            }catch(Exception e) {
                log.error("Error creating properties from database with manual connection creation.", e);
            }
        } else {
            log.error("Could not load properties from the database because no spring.datasource properties were present");
        }

    }

    /**
     * An implementation of springs PropertySource class that sources from a
     * {@link DataBasedBasedProperties} instance which is java.util.Properties class that
     * pulls its data from the database..   
     *
     */
    static class DataBasePropertySource extends EnumerablePropertySource<DataBasedBasedProperties> {

        public DataBasePropertySource(Connection c){
            super("DataBasePropertySource",new DataBasedBasedProperties(c));
        }

        /* (non-Javadoc)
         * @see org.springframework.core.env.PropertySource#getProperty(java.lang.String)
         */
        @Override
        public Object getProperty(String name) {
            return getSource().get(name);
        }

        @Override
        public String[] getPropertyNames() {
            return getSource().getPropertyNames();
        }
    }

    /**
     * Pulls name and value strings from a database table named properties
     *
     */
    static class DataBasedBasedProperties extends Properties {
        private static final long serialVersionUID = 1L;

        private String[] propertyNames;
        public DataBasedBasedProperties(Connection con)
        {
            List<String> names = new ArrayList<String>();
            try(
                    Statement stmt = con.createStatement();
                    ResultSet rs = stmt.executeQuery("select name, value from properties");
            ){
                while(rs.next()) {
                    String name = rs.getString(1);
                    String value = rs.getString(2);
                    names.add(name);
                    setProperty(name, value);
                }
                propertyNames = names.toArray(new String[names.size()]);
            }catch(SQLException e) {
                throw new RuntimeException(e);
            }
        }

        public String[] getPropertyNames() {
            return propertyNames;
        }

    }
}
0

Source: https://habr.com/ru/post/1660068/


All Articles