[JAVA] How to get the setting value (property value) from the database in Spring Framework

When handling setting values in Spring Framework, setting values can be specified in property files, system properties (values specified by -D), OS environment variables, JNDI (Web application environment only), etc., and on the application side It can be obtained via @ Value or ʻEnvironment`.

For example, it looks like the following.

Setting value specification example(properties file)


services.user.url=http://user.service.com/{id}
services.company.url=http://company.service.com/{id}

Reference example of setting value


@Service
public class MyService {

  private final RestTemplate restTemplate = new RestTemplate();
  private final Environment environment;

  @Value("${services.user.url}") //Injection of setting value at bean generation
  private String userServiceUrl;

  public MyService(Environment environment) {
    this.environment = environment;
  }

  public User getUser(String id) {
    User user = restTemplate.getForObject(userServiceUrl, User.class, id);
    return user;
  }

  public Company getCompany(String id) {
    String companyServiceUrl = environment.getProperty("services.company.url"); //Get the set value at runtime
    Company company = restTemplate.getForObject(companyServiceUrl, User.class);
    return company;
  }

}

In Spring Boot, instead of "@Value", "[Type-safe Configuration Properties](https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/htmlsingle/#" I think it is common to use "boot-features-external-config-typesafe-configuration-properties)", but here ** intentionally ** " @ Value"is used as a property placeholder (" Specifies to use$ {...}` ”). (Although I will not explain it, the method introduced here is also applicable to "Type-safe Configuration Properties")

Spring Framwork does not provide a class to get the setting value from the database (unfortunately?), But the setting value is saved from the database because it is abstracted by a class called PropertySource. If you create a PropertySource to get the and apply it to ʻEnvironment`, you can refer to it from your application.

Creating a PropertySource to get the settings from the database

Let's create a class that gets the setting value from the database at startup, caches it, and updates the cache at any time (though thread safety seems suspicious ...).

package com.example.demo;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class JdbcPropertySource extends EnumerablePropertySource<DataSource> implements InitializingBean {

  private final JdbcTemplate jdbcTemplate;
  private final String[] tableNames;
  private Map<String, Object> properties = Collections.emptyMap();

  public JdbcPropertySource(String name, DataSource dataSource, String... tableNames) {
    super(name, dataSource);
    this.jdbcTemplate = new JdbcTemplate(dataSource);
    this.tableNames = tableNames.length == 0 ? new String[]{"t_properties"} : tableNames;
  }

  @Override
  public String[] getPropertyNames() {
    return properties.keySet().toArray(new String[0]);
  }

  @Override
  public Object getProperty(String name) {
    Map<String, Object> currentProperties = properties;
    return currentProperties.get(name);
  }

  @Override
  public void afterPropertiesSet() {
    load();
  }

  public void load() {
    Map<String, Object> loadedProperties = Stream.of(tableNames)
        .flatMap(tableName -> jdbcTemplate.queryForList("SELECT name, value FROM " + tableName).stream())
        .collect(Collectors.toMap(e -> (String) e.get("name"), e -> e.get("value")));
    this.properties = loadedProperties;
  }

}

Application to ʻEnvironment`

package com.example.actuatordemo;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

import javax.sql.DataSource;

@Configuration
public class MyConfiguration {

  //DataSource settings
  @Bean
  DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.H2)
        .setName("demo")
        .addScript("classpath:init-db.sql")
        .build();
  }

  //Setting of JdbcPropertySource created this time
  @Bean
  JdbcPropertySource jdbcPropertySource(DataSource dataSource) {
    return new JdbcPropertySource("jdbcProperties", dataSource);
  }

  //Bean definition to apply the JdbcPropertySource created this time to Environment
  @Bean
  static BeanFactoryPostProcessor environmentPropertySourcesCustomizer() {
    return bf -> {
      ConfigurableEnvironment environment = bf.getBean(ConfigurableEnvironment.class);
      JdbcPropertySource propertySource = bf.getBean(JdbcPropertySource.class);
      //Apply according to the following priority of OS environment variables (priority is determined according to requirements)
      environment.getPropertySources()
          .addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, propertySource);
    };
  }

  //Bean definition to enable property placeholders
  @Bean
  static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
  }

}

This time, I applied it to ʻEnvironment using the mechanism of BeanFactoryPostProcessor`. Is this the best method? Is not very confident. I would appreciate it if you could comment if there is a better way.

Database preparation

Since this entry uses the embedded database, prepare the SQL to input the table and data.

src/main/resources/init-db.sql


drop table t_properties if exists;
create table t_properties (
  name varchar(512) not null primary key,
  value text
);

insert into t_properties (name, value) values ('services.user.url','http://dev01/services/user/{id}');
insert into t_properties (name, value) values ('services.company.url','http://dev01/services/company/{id}');

Summary

In the project I'm currently working on (non-Spring Boot project ...), it may be required (possibly) to have some setting values in the database (from a system operation point of view), so what can I do technically? I thought about it and made a prototype. There seems to be some room for improvement such as guaranteeing thread safety, but I am relieved to know that it can be realized by using the extension points of Spring Framework.

Recommended Posts

How to get the setting value (property value) from the database in Spring Framework
How to get the date in java
How to get Class from Element in Java
How to get the value after "_" in Windows batch like Java -version
How to bind to property file in Spring Boot
[Spring Boot] How to refer to the property file
I want to get the value in Ruby
How to create your own annotation in Java and get the value
[Spring Boot] How to get properties dynamically from a string contained in a URL
[Java] How to get the maximum value of HashMap
[Android] How to get the setting language of the terminal
[Java] How to get the key and value stored in Map by iterative processing
How to get the class name / method name running in Java
[IOS] How to get the table name from AWS DynamoDB
How to retrieve the hash value in an array in Ruby
How to get the longest information from Twitter as of 12/12/2016
How to change the setting value of Springboot Hikari CP
[Rails] How to display information stored in the database in view
How to use Lombok in Spring
How to get parameters in Spark
How to set and use profile in annotation-based Configuration in Spring framework
What I did in the migration from Spring Boot 1.4 series to 2.0 series
How to get the id of PRIMAY KEY auto_incremented in MyBatis
How to get boolean value with jQuery in rails simple form
What I did in the migration from Spring Boot 1.5 series to 2.0 series
How to get and add data from Firebase Firestore in Ruby
[Java] How to convert from String to Path type and get the path
[Rails] How to reset the database in production environment (Capistrano version)
[Android] I want to get the listener from the button in ListView
How to get the length of an audio file in java
How to increment the value of Map in one line in Java
[Spring Boot] I want to add my own property file and get the value with env.getProperty ().
How to get date data in Ruby
How to include Spring Tool in Eclipse 4.6.3?
[IOS] How to get data from DynamoDB
[Java] How to get the current directory
How to pass the value to another screen
[Swift] How to pass the Label value in the selected collectionViewCell to the destination TextField
How to get the current date as a string in yyyyMMdd format
Try to get data from database using MyBatis in Micronaut + Kotlin project
[Rails] How to get the user information currently logged in with devise
How to change the value of a variable at a breakpoint in intelliJ
How to get the absolute path of a directory running in Java
Android development, how to check null in the value of JSON object
How to bind request parameters in list format with bean property in Spring
[Swift] How to get the number of elements in an array (super basic)
How to get keycloak credentials in interceptor class
graphql-ruby: How to get the name of query or mutation in controller Note
How to check the logs in the Docker container
How to get a heapdump from a Docker container
How to get the ID of a user authenticated with Firebase in Swift
How to dump from database (DB) to seeds file
How to check the latest version of io.spring.platform to describe in pom.xml of Spring (STS)
How to add sound in the app (swift)
How to get today's day of the week
How to delete the database when recreating the application
Map GET requests to complex objects in Spring.
How to get SIMD optimization from HotSpot JavaVM
How to set environment variables in the properties file of Spring boot application
How to get the date from the JavaScript Date type that C # developers are addicted to
[Java] How to get the redirected final URL