Spring Boot - 加载初始数据

时间:2016-06-26 16:20:20

标签: spring spring-boot spring-data

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

例如,我有一个域模型" User"我可以通过访问/ 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"));
    }
}

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

19 个答案:

答案 0 :(得分:179)

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

minimum-height: 100vh;

同样,您也可以创建 schema.sql 文件(或schema-h2.sql)来创建架构:

INSERT INTO users (username, firstname, lastname) VALUES
  ('lala', 'lala', 'lala'),
  ('lolo', 'lolo', 'lolo');

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

CREATE TABLE task (
  id          INTEGER PRIMARY KEY,
  description VARCHAR(64) NOT NULL,
  completed   BIT NOT NULL);

有关Database initialization的文档可以找到更多信息。

如果您正在使用 Spring boot 2 ,则数据库初始化仅适用于嵌入式数据库(H2,HSQLDB,...)。如果您还想将其用于其他数据库,则需要更改spring.jpa.hibernate.ddl-auto=none 属性:

spring.datasource.initialization-mode

答案 1 :(得分:57)

如果我只想插入简单的测试数据,我经常会实现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"));
    }
}

答案 2 :(得分:22)

建议试试这个:

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

选项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;
}

scripts文件夹位于resources文件夹(IntelliJ Idea)

希望它有助于某人

答案 3 :(得分:15)

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

spring.datasource.data=classpath:accounts.sql, classpath:books.sql, classpath:reviews.sql

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

答案 4 :(得分:10)

您可以使用以下内容:

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

答案 5 :(得分:7)

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

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

另见:

答案 6 :(得分:6)

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

  

import.sql

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

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

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

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

答案 7 :(得分:5)

这是我得到的方式:

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

}

感谢本文作者:

答案 8 :(得分:4)

您只需在import.sql中创建一个src/main/resources文件,Hibernate将在创建架构时执行该文件。

答案 9 :(得分:2)

我通过这种方式解决了类似的问题:

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

答案 10 :(得分:1)

你可以注册和事件监听器来实现这一点,如下所示:

@EventListener
public void seed(ContextRefreshedEvent event) {
    userRepository.save(new User("lala", "lala", "lala"));
}

当触发ContextRefreshEvent时,我们可以访问应用程序中的所有自动装配的bean - 包括模型和存储库。

答案 11 :(得分:1)

您快到了!

@Component
public class DataLoader implements CommandLineRunner {

    private UserRepository userRepository;

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

    @Override
    public void run(String... args) throws Exception {
         LoadUsers()
    }

    private void LoadUsers() {
        userRepository.save(new User("lala", "lala", "lala"));
    }
}

答案 12 :(得分:1)

如果有人在accepted answer之后努力使它正常工作,对我来说,仅在我的src/test/resources/application.yml中添加H2 datasource详细信息:

spring:
  datasource:
    platform: h2
    url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password:

答案 13 :(得分:0)

最紧凑的(用于动态数据)将@ mathias-dpunkt解决方案放入MainApp(带有Lombok None):

@AllArgsConstructor

答案 14 :(得分:0)

如果您只想插入几行,并且您拥有JPA设置程序。您可以在下面使用

    @SpringBootApplication
        @Slf4j
        public class HospitalManagementApplication {

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

            @Bean
            ApplicationRunner init(PatientRepository repository) {
                return (ApplicationArguments args) ->  dataSetup(repository);
            } 

            public void dataSetup(PatientRepository repository){
            //inserts

     }

答案 15 :(得分:0)

这也可以。

    @Bean
    CommandLineRunner init (StudentRepo studentRepo){
        return args -> {
            // Adding two students objects
            List<String> names = Arrays.asList("udara", "sampath");
            names.forEach(name -> studentRepo.save(new Student(name)));
        };
    }

答案 16 :(得分:0)

您可以使用以下代码。在以下代码中,在Spring Boot应用程序启动期间发生数据库插入。

@SpringBootApplication
public class Application implements CommandLineRunner {
    
    @Autowired
    private IService<Car> service;

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

    @Override
    public void run(String... args) throws Exception {
        for(int i=1; i<=1000; i++) {
            Car car = new Car();
            car.setName("Car Name "+i);
            book.setPrice(50 + i);
            service.saveOrUpdate(car);
        }
    }

}

答案 17 :(得分:0)

一种可能是使用错误的JDBC URL。确保它是jdbc:h2:mem:testdb

答案 18 :(得分:0)

我创建了一个便于在Spring Boot应用程序中加载初始/演示数据的库。您可以在https://github.com/piotrpolak/spring-boot-data-fixtures

上找到它

一旦数据夹具启动器位于类路径上,它将在应用程序启动时自动尝试加载DICTIONARY数据(此行为可由属性控制)-您要做的就是注册一个实现{ {1}}。

我发现通过代码加载初始数据要优于使用SQL脚本加载初始数据:

  • 固定装置的逻辑与您的应用程序逻辑/域模型非常接近,并且随着域的发展而进行重构
  • 您将从增量演示数据更新中受益-想象一个QA环境,其中包含一些用户数据(在部署应用程序后不必丢失),但同时您想为自己开发的新功能添加数据

数据夹具示例:

DataFixture

该概念的灵感来自Symfony主义数据夹具套件。