3.2.4 使用 CommandLineRunner 预加载数据

使用 JdbcTemplate 时,我们在应用程序启动时使用 data.sql 预加载了 Ingredient 数据,在创建数据源 bean 时对数据库执行 插入操作。同样的方法也适用于 Spring Data JDBC。事实上,它对任何关系型数据库的持久化都有效。但是让我们看看另一种在启动时填充数据库的更灵活的方法。

Spring Boot 为应用程序启动时执行一些逻辑,提供了两个有用的接口:CommandLineRunner 和 ApplicationRunner。这两个函数式接口非常相似。都需要实现 run() 方法。当应用程序启动时,应用程序上下文中实现了 CommandLineRunner 或 ApplicationRunner 接口的任何 bean,将在应用程序上下文和所有 bean 生成之后,在其他任何事情发生之前,调用它们的 run() 方法。这为我们提供了一个方便的地方,以加载预置数据到数据库中。

因为 CommandLineRunner 和 ApplicationRunner 都是函数式接口,可以很方便的使用 @Bean 注解,在配置类中声明为 Bean,且返回一个 lambda 函数。例如,数据加载 CommandLineRunner bean 的例子:

@Bean
public CommandLineRunner dataLoader(IngredientRepository repo) {
  return args -> {
    repo.save(new Ingredient("FLTO", "Flour Tortilla", Type.WRAP));
    repo.save(new Ingredient("COTO", "Corn Tortilla", Type.WRAP));
    repo.save(new Ingredient("GRBF", "Ground Beef", Type.PROTEIN));
    repo.save(new Ingredient("CARN", "Carnitas", Type.PROTEIN));
    repo.save(new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES));
    repo.save(new Ingredient("LETC", "Lettuce", Type.VEGGIES));
    repo.save(new Ingredient("CHED", "Cheddar", Type.CHEESE));
    repo.save(new Ingredient("JACK", "Monterrey Jack", Type.CHEESE));
    repo.save(new Ingredient("SLSA", "Salsa", Type.SAUCE));
    repo.save(new Ingredient("SRCR", "Sour Cream", Type.SAUCE));
  };
}
`

这里,IngredientRepository 被注入 bean 方法中,并在 lambda 中创建 Ingredient 对象。CommandLineRunner 的 run() 方法接受单个参数,该参数是字符串变量,其中包含所有运行应用的命令行参数。我们不需要这些配料对象加载到数据库中,因此 args 参数可以忽略。

或者,我们可以将数据加载器 bean 定义为 ApplicationRunner 的 lambda 实现,如下所示:

@Bean
public ApplicationRunner dataLoader(IngredientRepository repo) {
  return args -> {
    repo.save(new Ingredient("FLTO", "Flour Tortilla", Type.WRAP));
    repo.save(new Ingredient("COTO", "Corn Tortilla", Type.WRAP));
    repo.save(new Ingredient("GRBF", "Ground Beef", Type.PROTEIN));
    repo.save(new Ingredient("CARN", "Carnitas", Type.PROTEIN));
    repo.save(new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES));
    repo.save(new Ingredient("LETC", "Lettuce", Type.VEGGIES));
    repo.save(new Ingredient("CHED", "Cheddar", Type.CHEESE));
    repo.save(new Ingredient("JACK", "Monterrey Jack", Type.CHEESE));
    repo.save(new Ingredient("SLSA", "Salsa", Type.SAUCE));
    repo.save(new Ingredient("SRCR", "Sour Cream", Type.SAUCE));
  };
}

CommandLineRunner 和 ApplicationRunner 之间的关键区别在于,传递给 run() 方法的参数。CommandLineRunner 接受字符串变量,该字符串命令行上传递的参数的原始表示形式。但是 ApplicationRunner 接受 ApplicationArguments 参数,作为命令行解析组件,该参数提供了访问参数的一些方法。

例如,假设我们希望应用程序接受命令行中所带的参数,比如“--version 1.2.3”,需要在我们的加载器 bean 中使用这个参数。如果使用 CommandLineRunner,我们需要在数组中搜索“-version”,然后提取紧接其后的数组中的值。但是使用 ApplicationRunner,我们可以查询定的 ApplicationArguments,直接使用“--version”参数。如下所示:

public ApplicationRunner dataLoader(IngredientRepository repo) {
  return args -> {
    List<String> version = args.getOptionValues("version");
    ...
  };
}

getOptionValues() 方法返回一个 List<String>,以允许选项参数可以多次指定。

但是,无论是 CommandLineRunner 还是 ApplicationRunner,我们都不需要命令行参数加载数据。因此,args 参数在我们的数据加载器 bean 中被忽略。

使用 CommandLineRunner 或 ApplicationRunner 进行初始数据加载的好处,是他们使用存储库来创建对象,而不是使用 SQL 脚本。这意味着它们对关系数据库或非关系数据库同样有效。在下一章中,当我们看到如何使用 Spring Data 持久化到非关系数据库,这就会显得非常方便。

但在此之前,让我们先看一看另一个 Spring Data 持久化关系数据库数据的项目:Spring Data JPA。

results matching ""

    No results matching ""