Spring Boot - загрузка начальных данных

181

Мне интересно, как лучше загрузить исходные данные базы данных перед запуском приложения? То, что я ищу, это то, что заполнит мою базу данных H2 данными.

Например, у меня есть модель домена «Пользователь». Я могу получить доступ к пользователям, перейдя в / users, но изначально в базе данных пользователей не будет, поэтому мне нужно их создать. Есть ли способ автоматически заполнять базу данных данными?

На данный момент у меня есть Бин, который создается экземпляром контейнера и создает пользователей для меня.

Пример:

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

Но я очень сомневаюсь, что это лучший способ сделать это. Либо это?

Lithicas
источник
4
Это сработает, или просто добавит data.sqlи / или schema.sqlинициализирует данные. Все это задокументировано в справочном руководстве (которое я предлагаю прочитать).
М. Дейнум
Пожалуйста, отметьте правильный ответ, если это помогло вам.
Reborn
Кто-нибудь получил это на работу? Я все еще не могу собрать это воедино и не уверен, что мне здесь не хватает. git.io/v5SWx
srini

Ответы:

297

Вы можете просто создать файл data.sql в папке src / main / resources, и он будет автоматически выполнен при запуске. В этом файле вы просто добавляете несколько операторов вставки, например:

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

Точно так же вы можете создать файл schema.sql (или schema-h2.sql), чтобы создать свою схему:

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

Хотя обычно вам не нужно этого делать, поскольку Spring boot уже настраивает Hibernate для создания вашей схемы на основе ваших сущностей для базы данных в памяти. Если вы действительно хотите использовать schema.sql, вам придется отключить эту функцию, добавив ее в ваш application.properties:

spring.jpa.hibernate.ddl-auto=none

Более подробную информацию можно найти в документации по инициализации базы данных .


Если вы используете Spring boot 2 , инициализация базы данных работает только для встроенных баз данных (H2, HSQLDB, ...). Если вы хотите использовать его и для других баз данных, вам нужно изменить spring.datasource.initialization-modeсвойство:

spring.datasource.initialization-mode=always

Если вы используете несколько поставщиков баз данных, вы можете назвать свой файл data-h2.sql или data-mysql.sql в зависимости от того, какую платформу базы данных вы хотите использовать.

Чтобы это работало, вам нужно настроить spring.datasource.platformсвойство:

spring.datasource.platform=h2
g00glen00b
источник
Спасибо @ g00glen00b за разъяснение: «и оно будет автоматически выполнено при запуске». Я получал ошибки, когда включал файл data.sql в конфигурацию моего компонента с помощью опции addScript (s). На данный момент схема еще не была построена.
Бенджамин Слабберт
5
@nespapu Вы ошибаетесь, однако, файлы schema.sql/ data.sqlбудут выполняться, когда spring.datasource.initializeесть true(это значение по умолчанию). spring.jpa.hibernate.ddl-autoможет использоваться для генерации ваших таблиц на основе конфигурации вашей сущности, а не с помощью файла SQL. Это по умолчанию включено в базах данных в памяти. Вот почему я добавил примечание в своем ответе, объясняющее, что если вы используете базу данных в памяти и хотите использовать ее schema.sql, вам нужно отключить, spring.jpa.hibernate.ddl-autoиначе оба будут пытаться создать вашу таблицу.
g00glen00b
7
Если вы хотите использовать data-h2.sqlимя файла для ваших начальных данных, вы должны также установить его spring.datasource.platform=h2в свойствах приложения.
Джейсон Эванс
1
Файл data.sql выполняется каждый раз, когда запускается приложение весенней загрузки. Это означает, что если у вас есть операторы вставки, они могут вызвать org.h2.jdbc.JdbcSQLExceptionисключение, потому что данные уже присутствуют в базе данных. Я использую встроенную базу данных H2, но проблема остается той же.
Игорь
1
@ g00glen00b к сожалению, это очень просто, потому что, например, у базы данных H2 есть проблемы MERGE INTO. Я понял, что есть способ обойти это, используя файл import.sql вместо data.sql . Она требует , spring.jpa.hibernate.ddl-autoчтобы создать или создать падение . Затем каждый раз, когда создается файл схемы (и / или выполняется схема.sql ), import.sql . Тем не менее: это похоже на обходной путь, а не чистую реализацию создания данных инициализации.
Игорь
82

Если я просто хочу вставить простые тестовые данные, я часто реализую 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"));
    }
}
Матиас Дпункт
источник
33

Вы можете добавить spring.datasource.dataсвойство в application.propertiesсписок файлов sql, которые вы хотите запустить. Как это:

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

Затем будут выполняться операторы вставки sql в каждом из этих файлов, что позволит вам поддерживать порядок.

Если вы поместите файлы в classpath, например, src/main/resourcesони будут применены. Или заменить classpath:на file:и использовать абсолютный путь к файлу

robjwilkins
источник
5
если вы хотите внешний файл, не забудьте поставить file:вместо classpath:.
Александр Лех
где должны находиться файлы (account.sql, ...)?
дпелисек
1
@dpelisek src / main / resources должен работать. Ответ обновлен.
Робжвилкинс
32

В качестве предложения попробуйте это:

@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
Вариант 2 великолепен, поскольку дает явное доказательство того, что происходит. Особенно для нескольких источников данных может потребоваться отключить SpringSataSourceAutoConfiguration.class, и в этом случае все остальные представленные здесь решения data.sql и schema.sql перестают работать.
Кайкарно
1
Если вы хотите загрузить исходные данные, но по-прежнему хотите, чтобы Hibernate создал DDL, но у вас есть несколько источников данных и они настроены вручную, тогда лучшим вариантом в этом случае является объявление компонента Spring DataSourceInitializer в соответствии со stackoverflow.com/a/23036217/3092830 как это займет проблема @PostConstruct для вас.
Кайкарно,
14

Вы можете использовать что-то вроде этого:

@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"));
      };
   }
}
Grauzone
источник
11

Spring Boot позволяет использовать простой скрипт для инициализации вашей базы данных, используя Spring Batch .

Тем не менее, если вы хотите использовать что-то более сложное для управления версиями БД и т. Д., Spring Boot хорошо интегрируется с Flyway .

Смотрите также:

Экстремальный байкер
источник
6
предложение весенней партии здесь кажется излишним.
Ник
@ Ник, ОП не упоминает количество данных .. В любом случае, ответ не совсем о весенней партии.
экстремальный байкер
На мой взгляд, Flyway или Liquibase - правильный путь. Не уверен насчет комментария Ника и, более того, комментариев по поводу / src / main / resources. Да, последний будет работать для небольших проектов. Ответ Xtreme Biker дает с помощью очень небольших усилий гораздо больше функциональности.
Александрос
10

В Spring Boot 2 data.sql не работал со мной, как в весенней загрузке 1.5

import.sql

Кроме того, файл с именем import.sql в корне пути к классам, выполняется при запуске, если Hibernate создает схему с нуля (то есть, если для свойства ddl-auto установлено значение create или create-drop).

Обратите внимание, очень важно, если вы вставляете ключи не могут быть продублированы, не используйте ddl-auto, свойство устанавливается на обновление, потому что при каждом перезапуске будут вставляться одни и те же данные снова

Для получения дополнительной информации вы посетите весенний веб-сайт

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

Ismail
источник
В Spring 2 инициализация базы данных работает только для встроенных баз данных. Если вы хотите использовать ее для других баз данных, вам нужно указать spring.datasource.initialization-mode = всегда
Edu Costa
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
    }

}

Спасибо автору этой статьи:

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

adkl
источник
Это не работает, если вы используете сервис, и если сервис в репозитории
autowiring
5

Вы можете просто создать import.sqlфайл, src/main/resourcesи Hibernate выполнит его при создании схемы.

Франческо Папаньо
источник
4

Я решил подобную проблему следующим образом:

@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();
    }
}
Сумка
источник
1

Если кто-то изо всех сил пытается заставить это работать даже после принятого ответа , для меня только работа, добавляющая в мои детали src/test/resources/application.ymlH2 datasource:

spring:
  datasource:
    platform: h2
    url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password:
Dherik
источник
1

Вы можете зарегистрироваться и прослушивать события, чтобы добиться этого, как показано ниже:

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

Когда запускается ContextRefreshEvent, мы получаем доступ ко всем автоматически подключаемым bean-компонентам в приложении, включая модели и репозитории.

Ахмед Ахмед
источник
1

Если вы хотите вставить только несколько строк и у вас есть JPA Setup. Вы можете использовать ниже

    @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

     }
Нирадж Сонавейн
источник
1
Я использовал это долго назад, не смог вспомнить. Это оно. Спасибо.
Фрилансер
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)));
        };
    }
Удара С.С. Лиянаге
источник
0

Самое компактное (для динамических данных) размещение решения @ mathias-dpunkt в MainApp (с Lombok @AllArgsConstructor):

@SpringBootApplication
@AllArgsConstructor
public class RestaurantVotingApplication implements ApplicationRunner {
  private final VoteRepository voteRepository;
  private final UserRepository userRepository;

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

  @Override
  public void run(ApplicationArguments args) {
    voteRepository.save(new Vote(userRepository.getOne(1), LocalDate.now(), LocalTime.now()));
  }
}
Григорий Кислин
источник
0

Ты почти там!

@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"));
    }
}
Эммануэль Осимосу
источник
0

Вы можете использовать приведенный ниже код. В следующем коде вставка базы данных происходит во время запуска приложения весенней загрузки.

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

}
Senthuran
источник