Spring Boot - Memuat Data Awal


179

Saya bertanya-tanya apa cara terbaik untuk memuat data basis data awal sebelum aplikasi dimulai? Apa yang saya cari adalah sesuatu yang akan mengisi database H2 saya dengan data.

Sebagai contoh, saya memiliki model domain "Pengguna" Saya dapat mengakses pengguna dengan mengunjungi / pengguna tetapi pada awalnya tidak akan ada pengguna dalam database sehingga saya harus membuatnya. Apakah ada yang mengisi database dengan data secara otomatis?

Saat ini saya memiliki Bean yang akan dipakai oleh wadah dan membuat pengguna untuk saya.

Contoh:

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

Tetapi saya sangat meragukan itu adalah cara terbaik untuk melakukannya. Atau itu?


4
Itu akan bekerja, atau cukup menambah data.sqldan / atau schema.sqluntuk init data .. Semua ini didokumentasikan dalam panduan referensi (yang saya sarankan untuk dibaca).
M. Deinum

Harap tandai jawaban yang benar jika itu membantu Anda.
Reborn

Adakah yang punya ini untuk bekerja? Saya masih tidak bisa menyatukan ini dan tidak yakin apa yang saya lewatkan di sini. git.io/v5SWx
srini

Jawaban:


294

Anda cukup membuat file data.sql di folder src / main / resources Anda dan secara otomatis akan dieksekusi saat startup. Dalam file ini Anda cukup menambahkan beberapa pernyataan insert, mis .: .:

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

Demikian pula, Anda dapat membuat file schema.sql (atau schema-h2.sql) juga untuk membuat skema Anda:

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

Meskipun biasanya Anda tidak harus melakukan ini karena boot Spring sudah mengkonfigurasi Hibernate untuk membuat skema Anda berdasarkan entitas Anda untuk dalam database memori. Jika Anda benar-benar ingin menggunakan schema.sql Anda harus menonaktifkan fitur ini dengan menambahkan ini ke application.properties Anda:

spring.jpa.hibernate.ddl-auto=none

Informasi lebih lanjut dapat ditemukan di dokumentasi tentang inisialisasi Database .


Jika Anda menggunakan Spring boot 2 , inisialisasi database hanya berfungsi untuk basis data tertanam (H2, HSQLDB, ...). Jika Anda ingin menggunakannya untuk database lain juga, Anda perlu mengubah spring.datasource.initialization-modeproperti:

spring.datasource.initialization-mode=always

Jika Anda menggunakan beberapa vendor basis data, Anda dapat memberi nama file data-h2.sql atau data-mysql.sql Anda tergantung pada platform basis data mana yang ingin Anda gunakan.

Untuk membuatnya bekerja, Anda harus mengkonfigurasi spring.datasource.platformproperti:

spring.datasource.platform=h2

Terima kasih @ g00glen00b untuk menjernihkan: "dan akan dieksekusi secara otomatis pada saat startup". Saya mendapatkan kesalahan karena saya memasukkan file data.sql dalam konfigurasi bean saya menggunakan opsi addScript. Sampai saat ini skema belum dibangun.
Benjamin Slabbert

5
@nespapu Anda memilikinya meskipun salah, schema.sql/ data.sqlfile akan dieksekusi ketika spring.datasource.initializeadalah true(yang merupakan default). spring.jpa.hibernate.ddl-autodapat digunakan untuk menghasilkan tabel Anda berdasarkan konfigurasi entitas Anda daripada menggunakan file SQL. Ini secara default diaktifkan pada database dalam memori. Itu sebabnya saya menambahkan catatan dalam jawaban saya, menjelaskan bahwa jika Anda menggunakan basis data dalam memori dan Anda ingin menggunakan schema.sql, Anda harus menonaktifkan spring.jpa.hibernate.ddl-autojika tidak keduanya akan mencoba membuat tabel Anda.
g00glen00b

7
Jika Anda ingin menggunakan data-h2.sqlnama file untuk data awal Anda, Anda harus mengatur spring.datasource.platform=h2properti aplikasi Anda juga.
Jason Evans

1
File data.sql dijalankan setiap kali aplikasi spring-boot dijalankan . Ini berarti bahwa jika Anda memiliki pernyataan penyisipan, mereka dapat menyebabkan org.h2.jdbc.JdbcSQLException-exepsi, karena data sudah ada dalam database. Saya menggunakan database H2 tertanam, tetapi masalahnya tetap sama.
Igor

1
@ g00glen00b sayangnya itu semua mudah, karena database H2 misalnya mengalami masalah MERGE INTO. Saya menemukan, bahwa ada cara untuk menghindari ini menggunakan file import.sql, bukan data.sql . Perlu spring.jpa.hibernate.ddl-autountuk membuat atau membuat-drop . Kemudian setiap kali file skema dibuat (dan / atau skema.sql dijalankan), import.sql juga dieksekusi. Tetap: ini terasa seperti solusi dan bukan implementasi yang bersih untuk menciptakan data init.
Igor

82

Jika saya hanya ingin memasukkan data pengujian sederhana, saya sering menerapkan ApplicationRunner. Implementasi antarmuka ini dijalankan pada saat startup aplikasi dan dapat menggunakan misal repositori yang diotorisasi untuk menyisipkan beberapa data uji.

Saya pikir implementasi semacam itu akan sedikit lebih eksplisit daripada milik Anda karena antarmuka menyiratkan bahwa implementasi Anda berisi sesuatu yang ingin Anda lakukan langsung setelah aplikasi Anda siap.

Implementasi Anda akan terlihat sth. seperti ini:

@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

Sebagai saran coba ini:

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

Opsi 2: Inisialisasi dengan skema dan skrip data

Prasyarat: application.propertiesAnda harus menyebutkan ini:

spring.jpa.hibernate.ddl-auto=none(Jika tidak, skrip akan diabaikan oleh hibernate, dan itu akan memindai proyek untuk @Entitydan / atau @Tablekelas yang dianotasi)

Kemudian, di MyApplicationkelas Anda tempel ini:

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

Di mana scriptsfolder berada di bawah resourcesfolder (IntelliJ Idea)

Semoga ini bisa membantu seseorang


3
Opsi 2 bagus karena memberikan bukti eksplisit tentang apa yang terjadi. Dengan beberapa sumber data terutama mungkin perlu menonaktifkan Spring's DataSourceAutoConfiguration.class dalam hal ini semua solusi data.sql dan schema.sql lain yang disediakan di sini berhenti berfungsi.
kaicarno

1
Jika Anda ingin memuat data awal tetapi masih ingin Hibernate membuat DDL tetapi Anda memiliki beberapa sumber data dan mengaturnya secara manual, maka opsi yang lebih baik dalam hal ini adalah mendeklarasikan kacang Spring DataSourceInitializer sesuai stackoverflow.com/a/23036217/3092830 karena akan mengambil masalah @PostConstruct untuk Anda.
kaicarno

32

Anda dapat menambahkan spring.datasource.dataproperti ke application.propertiesdaftar file sql yang ingin Anda jalankan. Seperti ini:

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

Pernyataan memasukkan sql di masing-masing file ini kemudian akan dijalankan, memungkinkan Anda untuk menjaga hal-hal tetap rapi.

Jika Anda meletakkan file di classpath, misalnya di src/main/resourcesmereka akan diterapkan. Atau ganti classpath:dengan file:dan gunakan jalur absolut ke file


5
jika anda ingin file eksternal jangan lupa untuk meletakkan file:bukan classpath:.
Aleksander Lech

di mana file (accounts.sql, ...) berada?
dpelisek

1
@dpelisek src / main / resources harus berfungsi. Jawaban diperbarui.
robjwilkins

14

Anda dapat menggunakan sesuatu seperti ini:

@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 memungkinkan Anda untuk menggunakan skrip sederhana untuk menginisialisasi database Anda, menggunakan Spring Batch .

Namun, jika Anda ingin menggunakan sesuatu yang sedikit lebih rumit untuk mengelola versi DB dan seterusnya, Spring Boot terintegrasi dengan baik dengan Flyway .

Lihat juga:


6
menyarankan spring batch di sini tampaknya berlebihan.
Nick

@Nick, OP tidak menyebutkan jumlah data .. Pokoknya jawabannya tidak semua tentang pegas batch.
Xtreme Biker

Menurut pendapat saya, Flyway atau Liquibase adalah cara yang tepat untuk dilakukan. Tidak yakin tentang komentar Nick dan lebih lanjut tentang upvote / src / main / resources. Ya, yang terakhir akan bekerja untuk proyek-proyek kecil. Jawaban Xtreme Biker memberikan melalui usaha yang sangat kecil sehingga lebih banyak fungsionalitas.
Alexandros

10

Di Spring Boot 2 data.sql tidak bekerja dengan saya seperti pada spring boot 1.5

import.sql

Selain itu, file bernama import.sqldi root classpath dieksekusi pada startup jika Hibernate membuat skema dari awal (yaitu, jika properti ddl-auto diatur untuk membuat atau membuat-drop).

Catatan sangat penting jika Anda memasukkan Kunci tidak dapat diduplikasi jangan gunakan properti ddl-auto diatur untuk memperbarui karena dengan setiap restart akan memasukkan data yang sama lagi

Untuk informasi lebih lanjut Anda mengunjungi websit musim semi

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


Di Spring 2, inisialisasi database hanya berfungsi untuk database yang disematkan. Jika Anda ingin menggunakannya untuk database lain, Anda perlu menentukan spring.datasource.initialization-mode = selalu
Edu Costa

6

Inilah cara saya mendapatkannya:

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

}

Terima kasih kepada penulis artikel ini:

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


Ini tidak berfungsi jika Anda menggunakan layanan, dan jika layanan di repositori autowiring
silentsudo

5

Anda cukup membuat import.sqlfile di src/main/resourcesdan Hibernate akan menjalankannya ketika skema dibuat.


4

Saya memecahkan masalah serupa dengan cara ini:

@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

Jika seseorang berjuang untuk membuatnya bekerja bahkan mengikuti jawaban yang diterima , bagi saya hanya bekerja dengan menambahkan rincian src/test/resources/application.ymlH2 saya datasource:

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

1

Anda dapat mendaftar dan pendengar acara untuk mencapai itu seperti di bawah ini:

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

Ketika ContextRefreshEvent dipecat, kami mendapatkan akses ke semua kacang autowired dalam aplikasi - termasuk model dan repositori.


1

Jika Anda ingin memasukkan hanya beberapa baris dan Anda memiliki Pengaturan JPA. Anda bisa menggunakan di bawah ini

    @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
Saya menggunakan ini kembali, tidak dapat mengingat. Ini dia. Terima kasih.
Freelancer

0

Ini juga akan berfungsi.

    @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

Solusi yang paling ringkas (untuk data dinamis) memasukkan @ mathias-dpunkt ke MainApp (dengan 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

Kamu hampir sampai!

@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

Anda dapat menggunakan kode di bawah ini. Dalam kode berikut penyisipan basis data terjadi selama permulaan aplikasi boot musim semi.

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

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.