如何 运行 SQL 脚本并在应用程序启动时获取数据?

How to run SQL scripts and get data on application startup?

我正在开发 Spring 引导应用程序。目前我的一些配置是硬编码的(例如 Hystrix 属性)。

所以我想在我的应用程序启动时或之后立即获取这些配置。

是否可以使用 Spring 引导来做到这一点?我的意思是 运行 SQL 启动脚本并获取数据。

如何在我的应用程序中检索和存储 properties/configs?

我正在使用 MyBatis 和 Oracle DB。

默认情况下,Spring-引导加载 data.sql and/or data-${platform}.sql.

但是,请记住脚本会在每次启动时加载,所以我认为更有意义(至少对于生产而言),让数据库中已经存在的值,而不是重新插入在每次开始。 我个人只在使用内存数据库时将数据库初始化用于 test/dev 目的。 尽管如此,这是 Spring-Boot.

来源:spring-boot-howto-database-initialization:

Spring JDBC has a DataSource initializer feature. Spring Boot enables it by default and loads SQL from the standard locations schema.sql and data.sql (in the root of the classpath). In addition Spring Boot will load the schema-${platform}.sql and data-${platform}.sql files (if present).

src/main/resources/数据-oracle.sql:

insert into...
insert into...
  • 您可以定义平台:spring.datasource.platform=oracle
  • 您可以更改要加载的 sql 脚本的名称:spring.datasource.data=myscript.sql
  • data.sql 一起,Spring-boot 还加载 schema.sql(在 data.sql 之前)。
  • 您的 data.sql 中也可以有一个 "update or insert" 逻辑:oracle sql: update if exists else insert

如果您想根据某些业务逻辑插入数据,我建议您使用事件监听器。所以基本上在应用程序启动时 "OnApplicationEvent" 因为它被 @EventListener 方法注释,所以将被自动调用。

另外,在您的情况下,您需要获取数据,您只需使用存储库对象来获取数据即可。

这是一个例子:

@Component
public class OnApplicationStartUp {

   @Autowired
   private ServiceRepository repository;


   @EventListener
   public void onApplicationEvent(ContextRefreshedEvent event) {

       //Write your business logic here.
       if (repository.findAll().size() <= 0) {
           preloadData();
       }else{
           fetchData();
       }
   }

    private void preloadData() {

       List<Service> services = new ArrayList<>();
       Service someService= new Service("name", "type");
       services.add(someService);
       ...
       ...
       repository.saveAll(services);
   }
}

如果您从 application.properties 文件获取,您可以使用环境 class。 像那样

Autowired
private Environment environment;
...
environment.getProperty("propertyName")

或者您可以定义自己的 属性 文件。那么你可以从中得到 @PropertySource(name = "myProperties", value = "example.properties") 注释

您需要使用@Value 注解从您定义的属性 文件中获取特定值。

@Value("${propertyNameInYourPropertFile}")
private String url;

并且你想在应用程序刚启动时启动一些东西,你可以在方法之前使用它

@EventListener(ApplicationReadyEvent.class)

但是需要使用@Service或者@Component注解,Class里面有这个方法。

完全可以,你可以用这个。

example.properties :

url=yourValue
userName=yourDBUserName
password=yourDBPassword

示例 class :

@Service
@PropertySource(name = "myProperties", value = "example.properties")
public class Start{

    @Value("${url}")
    private String url;

    @Value("${userName}")
    private String userName;

    @Value("${password}")
    private String password;


    //Run this method when application started
    @EventListener(ApplicationReadyEvent.class)
    public ResultSet getConnection()
    {

        //Connect to Database
        Connection connection = null;
        String QUERY="your sql query";
        try {
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            connection = DriverManager.getConnection(url, userName, password );
        } catch (SQLException e) {
        }


        //Run your query
        Statement stmt = null;
        try {
            stmt = connection.createStatement();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        ResultSet rs = null;
        try {
            rs = stmt.executeQuery(QUERY);
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

        return rs;
    }

}

对我有用的是使用 DataSourceInitializer:

@Bean
public DataSourceInitializer dataSourceInitializer(@Qualifier("dataSource") final DataSource dataSource) {
    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
    resourceDatabasePopulator.addScript(new ClassPathResource("/data.sql"));
    DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
    dataSourceInitializer.setDataSource(dataSource);
    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
    return dataSourceInitializer;
}

Used to set up a database during initialization and clean up a database during destruction.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/init/DataSourceInitializer.html

  1. 定义一个 Spring Bean(例如 GlobalConfigurationBean)
    范围:@Scope(scopeName = WebApplicationContext.SCOPE_APPLICATION)
    此 Bean 将负责在 Bean 初始化期间从您的数据库 Table(维护配置属性)获取数据。

  2. 使用@PostConstruct 注释方法。
    该方法具有从数据库中获取配置参数的逻辑table。

  3. 此 bean 的范围将确保所需的配置仅从数据库中获取一次 table(例如使用 Hibernate 查询或纯本机 SQL)和
    可用于同一应用程序中不同上下文中的所有 bean。

现在只需将此 bean 注入到您希望使用这些配置属性或参数的任何位置。

要么

使用:: Apache Commons DatabaseConfiguration <--
注意:: 它不支持缓存。但我猜你不需要缓存,因为数据库配置属性应该只在应用程序启动时加载一次。

要么

传统的旧方法:通过扩展定义 "PropertyPlaceHolderConfigurer" 的自定义实现,并将其定义为 Spring Bean。
此实现应该具有从您的数据库 table 中获取数据的逻辑,该数据库包含配置属性。

Initialize a Database using basic SQL scripts

仅使用基本 SQL 脚本时,Spring Boot 会自动创建嵌入式数据源的架构。可以使用 spring.datasource.initialization-模式 属性 自定义此行为。例如,如果您希望始终初始化 DataSource 而不管其类型:

spring.datasource.initialization-mode=always

如果要在启动后从 sql 脚本加载数据,请使用 ResourceDatabasePopulator class 对象,如下所示。

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

import javax.sql.DataSource;

@Component
public class InitializeData {

    @Autowired
    private DataSource dataSource;

    @EventListener(ApplicationReadyEvent.class)
    public void loadData() {
            ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator(false, false, "UTF-8", new ClassPathResource("data.sql"));
        resourceDatabasePopulator.execute(dataSource);
    }
}

它会很容易地从 sql 文件加载数据,不用担心 sql 文件中的错误 sql 语句,因为它会被忽略。