Spring Boot - 加载初始数据

Lit*_*cas 143 spring spring-data spring-boot

我想知道在应用程序启动之前加载初始数据库数据的最佳方法是什么?我正在寻找的东西将使我的H2数据库充满数据.

例如,我有一个域模型"用户"我可以通过转到/ users来访问用户,但最初在数据库中不会有任何用户,所以我必须创建它们.反正有没有自动填充数据库?

目前我有一个容器实例化的Bean,并为我创建用户.

例:

@Component
public class DataLoader {

    private UserRepository userRepository;

    @Autowired
    public DataLoader(UserRepository userRepository) {
        this.userRepository = userRepository;
        LoadUsers();
    }

    private void LoadUsers() {
        userRepository.save(new User("lala", "lala", "lala"));
    }
}
Run Code Online (Sandbox Code Playgroud)

但我非常怀疑这是最好的方法.或者是吗?

g00*_*00b 226

您可以在src/main/resources文件夹中创建一个data.sql文件(或者如果您只希望在H2是您的数据库时应用它的数据-h2.sql),它将在启动时自动执行.在这个文件中你只需添加一些插入语句,例如:

INSERT INTO users (username, firstname, lastname) VALUES
  ('lala', 'lala', 'lala'),
  ('lolo', 'lolo', 'lolo');
Run Code Online (Sandbox Code Playgroud)

同样的,你可以创建一个schema.sql文件文件(或架构h2.sql),以及创建您的模式:

CREATE TABLE task (
  id          INTEGER PRIMARY KEY,
  description VARCHAR(64) NOT NULL,
  completed   BIT NOT NULL);
Run Code Online (Sandbox Code Playgroud)

虽然通常你不应该这样做,因为Spring引导已经配置Hibernate来创建基于你的内存数据库实体的模式.如果您确实想使用schema.sql,则必须通过将此功能添加到application.properties来禁用此功能:

spring.jpa.hibernate.ddl-auto=none
Run Code Online (Sandbox Code Playgroud)

有关数据库初始化的文档可以找到更多信息.


如果您使用的是Spring boot 2,则数据库初始化仅适用于嵌入式数据库(H2,HSQLDB,...).如果您还想将其用于其他数据库,则需要更改spring.datasource.initialization-mode属性:

spring.datasource.initialization-mode=always
Run Code Online (Sandbox Code Playgroud)

  • 如果要对初始数据使用`data-h2.sql`文件名,则还应在应用程序属性中设置`spring.datasource.platform = h2`. (7认同)
  • @nespapu你错了,``schema.sql` /`data.sql`文件将在`spring.datasource.initialize`为'true`(这是默认值)时执行.`spring.jpa.hibernate.ddl-auto`可用于根据实体配置生成表,而不是使用SQL文件.默认情况下,在内存数据库中启用此功能.这就是为什么我在我的回答中添加了注释,解释说如果你使用内存数据库而你想使用`schema.sql`,你需要禁用`spring.jpa.hibernate.ddl-auto`否则两者都将尝试创建你的表. (5认同)
  • **每次** spring-boot 应用程序启动时都会执行 data.sql 文件。这意味着如果您有插入语句,它们可能会导致“org.h2.jdbc.JdbcSQLException”异常,因为**数据已经存在于数据库中**。我正在使用嵌入式 H2 数据库,但问题仍然存在。 (2认同)
  • @g00glen00b 遗憾的是,这几乎很容易,因为例如 H2 数据库在“MERGE INTO”方面存在问题。我发现有一种方法可以使用 **import.sql** 文件而不是 **data.sql** 来规避此问题。它需要`spring.jpa.hibernate.ddl-auto`来**创建**或**创建删除**。然后,每当创建架构文件(和/或执行 **schema.sql**)时,**import.sql** 也会执行。尽管如此:这感觉像是一种解决方法,而不是创建初始化数据的干净实现。 (2认同)

Mat*_*nkt 66

如果我只想插入简单的测试数据,我经常实现一个ApplicationRunner.此接口的实现在应用程序启动时运行,并且可以使用例如自动装配的存储库来插入一些测试数据.

我认为这样的实现会比你的更明确,因为界面意味着你的实现包含你想要在应用程序准备好后直接做的事情.

你的实现看起来很...... 像这样:

@Component
public class DataLoader implements ApplicationRunner {

    private UserRepository userRepository;

    @Autowired
    public DataLoader(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void run(ApplicationArguments args) {
        userRepository.save(new User("lala", "lala", "lala"));
    }
}
Run Code Online (Sandbox Code Playgroud)


Reb*_*orn 26

建议试试这个:

@Bean
public CommandLineRunner loadData(CustomerRepository repository) {
    return (args) -> {
        // save a couple of customers
        repository.save(new Customer("Jack", "Bauer"));
        repository.save(new Customer("Chloe", "O'Brian"));
        repository.save(new Customer("Kim", "Bauer"));
        repository.save(new Customer("David", "Palmer"));
        repository.save(new Customer("Michelle", "Dessler"));

        // fetch all customers
        log.info("Customers found with findAll():");
        log.info("-------------------------------");
        for (Customer customer : repository.findAll()) {
            log.info(customer.toString());
        }
        log.info("");

        // fetch an individual customer by ID
        Customer customer = repository.findOne(1L);
        log.info("Customer found with findOne(1L):");
        log.info("--------------------------------");
        log.info(customer.toString());
        log.info("");

        // fetch customers by last name
        log.info("Customer found with findByLastNameStartsWithIgnoreCase('Bauer'):");
        log.info("--------------------------------------------");
        for (Customer bauer : repository
                .findByLastNameStartsWithIgnoreCase("Bauer")) {
            log.info(bauer.toString());
        }
        log.info("");
    }
}
Run Code Online (Sandbox Code Playgroud)

选项2:使用模式和数据脚本进行初始化

先决条件:application.properties你必须提到这个:

spring.jpa.hibernate.ddl-auto=none(否则hibernate将忽略脚本,它将扫描项目@Entity和/或@Table注释类)

然后,在你的MyApplication课上粘贴这个:

@Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("org.h2.Driver");
    dataSource.setUrl("jdbc:h2:~/myDB;MV_STORE=false");
    dataSource.setUsername("sa");
    dataSource.setPassword("");

    // schema init
    Resource initSchema = new ClassPathResource("scripts/schema-h2.sql");
    Resource initData = new ClassPathResource("scripts/data-h2.sql");
    DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema, initData);
    DatabasePopulatorUtils.execute(databasePopulator, dataSource);

    return dataSource;
}
Run Code Online (Sandbox Code Playgroud)

scripts文件夹位于下的resources文件夹(的IntelliJ IDEA)

希望它可以帮助某人

  • 选项 2 很棒,因为它清楚地证明了正在发生的事情。特别是对于多个数据源,可能需要禁用 Spring 的 DataSourceAutoConfiguration.class,在这种情况下,此处提供的所有其他 data.sql 和 schema.sql 解决方案都将停止工作。 (3认同)

rob*_*ins 23

您可以添加spring.datasource.data属性以application.properties列出要运行的sql文件.像这样:

spring.datasource.data=classpath:accounts.sql, classpath:books.sql, classpath:reviews.sql
Run Code Online (Sandbox Code Playgroud)

然后将运行每个文件中的sql insert语句,使您可以保持整洁

  • 这些属性现已弃用,您应该使用 `spring.sql.init.data-locations` 和 `spring.sql.init.schema-locations` 代替。 (6认同)
  • 万一你想要一个外部文件,别忘了放`file:`而不是`classpath:`。 (2认同)
  • 还有用于 DDL 脚本的“spring.datasource.schema”。 (2认同)
  • 或者如果存在结构 resources/sql/file1.sql 和 file2.sql,则使用: spring.datasource.data=classpath:sql/file1.sql, classpath:sql/file2.sql (2认同)

小智 13

你可以使用这样的东西:

@SpringBootApplication  
public class Application {

@Autowired
private UserRepository userRepository;

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}

@Bean
InitializingBean sendDatabase() {
    return () -> {
        userRepository.save(new User("John"));
        userRepository.save(new User("Rambo"));
      };
   }
}
Run Code Online (Sandbox Code Playgroud)


Pan*_*kos 10

如果您来到这里,但似乎没有什么对您有用,那么您可能会受到随之而来的一些变化的影响Spring Boot 2.5

这是我用于 postgresql 的全部属性。

spring:
  sql.init.mode: always   <-----------------
  datasource:
    url: jdbc:postgresql://localhost:5432/products
    username: 
    password: 
  jpa:
    defer-datasource-initialization: true  <------------------
    hibernate:
      ddl-auto: create-drop   <----------------
    database-platform: org.hibernate.dialect.PostgreSQLDialect
Run Code Online (Sandbox Code Playgroud)

我还标记了<---当前主题的相关属性,以实现以下目的。

  • ORM 供应商将从 Java 实体模型为您创建数据库模式。
  • 创建数据库模式后,初始数据将从文件加载到数据库data.sql

Ps:不要忘记添加带有初始数据的文件, data.sqlsrc/main/resources

另可参考:Spring Boot 2.5 发行说明


Xtr*_*ica 8

Spring Boot允许您使用Spring Batch来使用简单的脚本初始化数据库.

尽管如此,如果你想使用一些更精细的东西来管理数据库版本等,Spring Boot可以很好地与Flyway集成.

也可以看看:

  • 建议春季批次似乎有点矫枉过正. (5认同)

Fra*_*gno 8

您可以简单地在其中创建一个import.sql文件src/main/resources,Hibernate 将在创建模式时执行它。


小智 7

在Spring Boot 2中,data.sql无法像Spring Boot 1.5中那样与我一起使用

import.sql

另外,import.sql如果Hibernate从头开始创建架构(即,如果ddl-auto属性设置为create或create-drop),则在启动时将执行在类路径的根目录中命名的文件。

请注意,如果您插入的键不能重复,请不要使用ddl-auto属性,将其设置为更新,因为每次重新启动都会再次插入相同的数据,这一点非常重要

有关更多信息,请访问spring网站。

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html


adk*_*dkl 6

这是我得到的方式:

@Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {

    /**
     * This event is executed as late as conceivably possible to indicate that
     * the application is ready to service requests.
     */

    @Autowired
    private MovieRepositoryImpl movieRepository;

    @Override
    public void onApplicationEvent(final ApplicationReadyEvent event) {
        seedData();
    }

    private void seedData() {
        movieRepository.save(new Movie("Example"));

        // ... add more code
    }

}
Run Code Online (Sandbox Code Playgroud)

感谢本文作者:

http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/


小智 6

我这样解决了类似的问题:

@Component
public class DataLoader {

    @Autowired
    private UserRepository userRepository;

    //method invoked during the startup
    @PostConstruct
    public void loadData() {
        userRepository.save(new User("user"));
    }

    //method invoked during the shutdown
    @PreDestroy
    public void removeData() {
        userRepository.deleteAll();
    }
}
Run Code Online (Sandbox Code Playgroud)