Spring Boot-初期データの読み込み


179

アプリケーションが開始する前に初期データベースデータをロードするための最良の方法は何ですか?私が探しているのは、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"));
    }
}

しかし、それが最善の方法であるかどうかは非常に疑問です。またはそれは?


4
これは機能します。または単にデータを追加data.sqlしたりschema.sql、初期化データに追加したりします。これはすべて、リファレンスガイドに記載されています(私はこれを読むことをお勧めします)。
M. Deinum 2016年

問題が解決した場合は、正解をマークしてください。
生まれ変わる

誰かがこれを機能させましたか?私はまだこれをまとめることができず、ここで何が欠けているのかわかりません。git.io/v5SWx
srini

回答:


294

src / main / resourcesフォルダーにdata.sqlファイルを作成するだけで、起動時に自動的に実行されます。このファイルでは、いくつかの挿入ステートメントを追加するだけです。例:

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ブートはメモリ内データベースのエンティティに基づいてスキーマを作成するように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をクリーンアップしていただきありがとうございます。「起動時に自動的に実行されます」。addScript(s)オプションを使用してBeanの構成にdata.sqlファイルを含めたため、エラーが発生しました。この時点では、スキーマはまだ構築されていません。
Benjamin Slabbert 2017

5
@nespapu間違っていますが、schema.sql/ data.sqlファイルspring.datasource.initializetrue(デフォルト)のときに実行されます。spring.jpa.hibernate.ddl-autoSQLファイルを使用するのではなく、エンティティ構成に基づいてテーブルを生成するために使用できます。これは、インメモリデータベースではデフォルトで有効になっています。そのため、回答にメモを追加しました。インメモリデータベースを使用していてschema.sql、を使用したい場合は、無効にする必要がありspring.jpa.hibernate.ddl-autoます。そうしないと、両方がテーブルを作成しようとします。
g00glen00b 2017年

7
data-h2.sql初期データにファイル名を使用する場合spring.datasource.platform=h2は、アプリケーションプロパティでも設定する必要があります。
Jason Evans

1
data.sqlファイルは、spring-bootアプリケーションが起動されるたびに実行されます。つまり、insertステートメントがある場合org.h2.jdbc.JdbcSQLExceptionデータがすでにデータベースに存在ているため、-exception が発生する可能性があります。組み込みH2データベースを使用していますが、問題は同じままです。
イゴール

1
@ g00glen00b悲しいことに、たとえばH2データベースではに問題があるため、それは簡単MERGE INTOです。data.sqlの代わりにimport.sqlファイルを使用してこれを回避する方法があることがわかりました。それは必要とspring.jpa.hibernate.ddl-autoして作成するか、作成ドロップします。その後、スキーマファイルが作成される(および/またはschema.sqlが実行される)たびに、import.sqlも実行されます。それでも、それは回避策のように感じられ、initデータを作成するクリーンな実装ではありません。
イゴール

82

単純なテストデータを挿入するだけの場合は、を実装することがよくありApplicationRunnerます。このインターフェイスの実装は、アプリケーションの起動時に実行され、たとえば、自動配線されたリポジトリを使用して、いくつかのテストデータを挿入できます。

インターフェースは、アプリケーションの準備ができた直後に実行したいことを実装が含んでいることをインターフェースが暗示するので、そのような実装はあなたの実装よりも少し明示的だと思います。

あなたの実装はsthに見えるでしょう。このような:

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

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は、何が起こっているかを明示的に証明できるので、優れています。特に複数のデータソースがある場合、SpringのDataSourceAutoConfiguration.classを無効にする必要がある場合があります。その場合、ここで提供される他のすべてのdata.sqlおよびschema.sqlソリューションが機能しなくなります。
kaicarno

1
初期データをロードしたいが、HibernateでDDLを作成したいが、複数のデータソースがあり、それらを手動で設定する場合、この場合のより良いオプションは、stackoverflow.com / a / 23036217/3092830に従ってSpringのDataSourceInitializer Beanを宣言することです。@PostConstructの問題が発生するためです。
kaicarno

32

実行するSQLファイルspring.datasource.dataapplication.propertiesリストするプロパティを追加できます。このような:

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

次に、これらの各ファイルのSQL挿入ステートメントが実行され、物事を整理しておくことができます。

たとえばファイルをクラスパスに置くと、src/main/resourcesそれらが適用されます。または、ファイルへの絶対パスに置き換えclasspath:file:使用します


5
外部ファイルが必要な場合は、のfile:代わりに配置することを忘れないでくださいclasspath:
Aleksander Lech

ファイル(accounts.sqlなど)はどこに配置する必要がありますか?
dpelisek

1
@dpelisek src / main / resourcesは動作するはずです。回答を更新しました。
robjwilkins

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

11

Spring Bootでは、Spring Batchを使用して、簡単なスクリプトでデータベースを初期化できます。

それでも、DBバージョンの管理などにもう少し複雑なものを使用したい場合、Spring BootはFlywayとうまく統合されます

以下も参照してください。


6
ここで春のバッチを提案するのはやり過ぎのようです。
Nick

@ Nick、OPはデータ量について言及していません。とにかく答えは春のバッチに関するすべてではありません。
Xtreme Biker

私の意見では、FlywayまたはLiquibaseが正しい方法です。Nickのコメントや、/ src / main / resourcesの賛成投票については不明です。はい、後者は小さなプロジェクトで機能します。Xtreme Bikerの答えは、非常に少ない労力で非常に多くの機能を提供します。
アレクサンドロス

10

Spring Boot 2では、data.sqlはSpring Boot 1.5のように機能しませんでした

import.sql

さらに、import.sqlHibernateが最初からスキーマを作成する場合(つまり、ddl-autoプロパティがcreateまたはcreate-dropに設定されている場合)、クラスパスのルートで指定されたファイルが起動時に実行されます。

キーを複製できない場合は非常に重要であることに注意してください。再起動するたびに同じデータが再度挿入されるため、ddl-autoプロパティはupdateに設定しないでください。

詳細については、春のウェブサイトをご覧ください

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


Spring 2では、データベースの初期化は埋め込みデータベースでのみ機能します。他のデータベースで使用する場合は、spring.datasource.initialization-mode = always
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/


これは、サービスを使用している場合、および
自動配線


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.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:

1

イベントリスナーを登録して、以下のように実現できます。

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

ContextRefreshEventが発生すると、モデルやリポジトリを含む、アプリケーション内のすべての自動ワイヤードBeanにアクセスできます。


1

数行のみを挿入したい場合は、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

     }

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.