Avvio a molla - Caricamento dei dati iniziali


180

Mi chiedo quale sia il modo migliore per caricare i dati del database iniziale prima dell'avvio dell'applicazione? Quello che sto cercando è qualcosa che riempirà il mio database H2 di dati.

Ad esempio, ho un modello di dominio "Utente" che posso accedere agli utenti andando su / utenti ma inizialmente non ci saranno utenti nel database, quindi devo crearli. Esiste un modo per riempire automaticamente il database di dati?

Al momento ho un Bean che viene istanziato dal contenitore e crea utenti per me.

Esempio:

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

Ma dubito fortemente che sia il modo migliore per farlo. O è?


4
Funzionerà, o semplicemente aggiungerà data.sqle / o schema.sqlper l'inizializzazione dei dati. Tutto questo è documentato nella guida di riferimento (che consiglio di leggere).
M. Deinum,

Contrassegna la risposta corretta se ciò ti è stato di aiuto.
Rinasce l'

Qualcuno ha avuto questo per funzionare? Non riesco ancora a metterlo insieme e non sono sicuro di cosa mi manchi qui. git.io/v5SWx
srini

Risposte:


296

Puoi semplicemente creare un file data.sql nella cartella src / main / resources e verrà eseguito automaticamente all'avvio. In questo file è sufficiente aggiungere alcune istruzioni di inserimento, ad es .:

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

Allo stesso modo, puoi creare un file schema.sql (o schema-h2.sql) e creare lo schema:

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

Anche se normalmente non dovresti farlo dal momento che Spring boot configura già Hibernate per creare il tuo schema basato sulle tue entità per un database in memoria. Se vuoi davvero usare schema.sql dovrai disabilitare questa funzione aggiungendola a application.properties:

spring.jpa.hibernate.ddl-auto=none

Ulteriori informazioni sono disponibili nella documentazione relativa all'inizializzazione del database .


Se si utilizza Spring boot 2 , l'inizializzazione del database funziona solo per i database incorporati (H2, HSQLDB, ...). Se si desidera utilizzarlo anche per altri database, è necessario modificare la spring.datasource.initialization-modeproprietà:

spring.datasource.initialization-mode=always

Se si utilizzano più fornitori di database, è possibile assegnare un nome al file data-h2.sql o data-mysql.sql a seconda della piattaforma del database che si desidera utilizzare.

Per farlo funzionare, dovrai configurare il spring.datasource.platform proprietà:

spring.datasource.platform=h2

Grazie @ g00glen00b per aver chiarito: "e verrà eseguito automaticamente all'avvio". Stavo ricevendo errori quando includevo il file data.sql nella configurazione del mio bean usando l'opzione addScript (s). A questo punto lo schema non era ancora stato creato.
Benjamin Slabbert,

5
@nespapu Hai sbagliato, però, i file schema.sql/ data.sqlverranno eseguiti quando spring.datasource.initializeè true(che è l'impostazione predefinita). spring.jpa.hibernate.ddl-autopuò essere utilizzato per generare le tabelle in base alla configurazione dell'entità anziché utilizzare un file SQL. Questo è abilitato di default sui database in memoria. Ecco perché ho aggiunto la nota nella mia risposta, spiegando che se si utilizza un database in memoria e si desidera utilizzare il schema.sql, è necessario disabilitare spring.jpa.hibernate.ddl-autoaltrimenti entrambi tenteranno di creare la tabella.
g00glen00b,

7
Se si desidera utilizzare il data-h2.sqlnome file per i dati iniziali, è necessario impostare anche spring.datasource.platform=h2le proprietà dell'applicazione.
Jason Evans,

1
Il file data.sql viene eseguito ogni volta che viene avviata l'applicazione di avvio a molla. Ciò significa che se si dispone di istruzioni insert, possono causare org.h2.jdbc.JdbcSQLExceptionun'eccezione, poiché i dati sono già presenti nel database. Sto usando un database H2 incorporato, ma il problema rimane lo stesso.
Igor,

1
@ g00glen00b purtroppo è quasi impossibile, perché il database H2, ad esempio, ha problemi MERGE INTO. Ho capito che esiste un modo per aggirare questo usando un file import.sql invece di un data.sql . Richiede spring.jpa.hibernate.ddl-autodi creare o creare-eliminare . Quindi ogni volta che viene creato il file di schema (e / o viene eseguito uno schema.sql ), viene eseguito anche import.sql . Tuttavia: sembra una soluzione alternativa e non un'implementazione pulita della creazione di dati init.
Igor,

82

Se voglio solo inserire semplici dati di test, spesso implemento a ApplicationRunner . Le implementazioni di questa interfaccia vengono eseguite all'avvio dell'applicazione e possono ad esempio utilizzare un repository autowired per inserire alcuni dati di test.

Penso che un'implementazione del genere sarebbe leggermente più esplicita della tua perché l'interfaccia implica che l'implementazione contenga qualcosa che vorresti fare direttamente dopo che l'applicazione è pronta.

L'implementazione sembrerebbe sth. come questo:

@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

Come suggerimento prova questo:

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

Opzione 2: inizializzare con script di schema e dati

Prerequisiti: application.propertiesdevi menzionare questo:

spring.jpa.hibernate.ddl-auto=none(altrimenti gli script verranno ignorati da ibernazione e scansionerà il progetto per @Entitye / o @Tableclassi annotate)

Quindi, nella tua MyApplicationclasse, incolla questo:

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

Dove scriptssi trova la resourcescartella sotto la cartella (IntelliJ Idea)

Spero che aiuti qualcuno


3
L'opzione 2 è eccezionale in quanto fornisce una prova esplicita di ciò che sta accadendo. Con più origini dati, in particolare, potrebbe essere necessario disabilitare DataSourceAutoConfiguration.class di Spring, nel qual caso tutte le altre soluzioni data.sql e schema.sql fornite qui smettono di funzionare.
Kaicarno,

1
Se vuoi caricare i dati iniziali ma vuoi ancora che Hibernate crei il DDL ma hai più origini dati e impostale manualmente, un'opzione migliore in questo caso è dichiarare il bean DataSourceInitializer di Spring come stackoverflow.com/a/23036217/3092830 come prenderà per te il problema di @PostConstruct.
Kaicarno,

32

È possibile aggiungere una spring.datasource.dataproprietà application.propertiesall'elenco dei file sql che si desidera eseguire. Come questo:

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

Verranno quindi eseguite le istruzioni di inserimento sql in ciascuno di questi file, che consente di mantenere le cose in ordine.

Se si inseriscono i file nel percorso di classe, ad esempio src/main/resources, verranno applicati. Oppure sostituire classpath:con file:e utilizzare un percorso assoluto per il file


5
nel caso in cui desideri un file esterno, non dimenticare di mettere file:invece di classpath:.
Aleksander Lech,

dove devono trovarsi i file (accounts.sql, ...)?
Dpelisek,

1
@dpelisek src / main / resources dovrebbe funzionare. Risposta aggiornata
robjwilkins,

14

Puoi usare qualcosa del genere:

@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 ti consente di utilizzare un semplice script per inizializzare il tuo database, usando Spring Batch .

Tuttavia, se si desidera utilizzare qualcosa di un po 'più elaborato per gestire le versioni DB e così via, Spring Boot si integra bene con Flyway .

Guarda anche:


6
suggerendo che il lotto di primavera qui sembra eccessivo.
Nick,

@Nick, l'OP non menziona la quantità di dati .. Comunque la risposta non riguarda tutto il batch primaverile.
Xtreme Biker,

Secondo me, Flyway o Liquibase è la strada giusta da percorrere. Non sono sicuro del commento di Nick e di ulteriori informazioni sui voti positivi di / src / main / resources. Sì, quest'ultimo avrebbe funzionato per piccoli progetti. La risposta di Xtreme Biker offre, grazie a un piccolo sforzo, molte più funzionalità.
Alexandros,

10

In Spring Boot 2 data.sql non funzionava con me come in Spring Boot 1.5

import.sql

Inoltre, un file denominato import.sqlnella radice del percorso di classe viene eseguito all'avvio se Hibernate crea lo schema da zero (ovvero se la proprietà ddl-auto è impostata su create o create-drop).

Nota molto importante se si inseriscono le chiavi non possono essere duplicate non utilizzare la proprietà ddl-auto è impostata per l'aggiornamento perché ad ogni riavvio si inseriranno nuovamente gli stessi dati

Per ulteriori informazioni, visitare il sito Web di Spring

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


Nella primavera 2 l'inizializzazione del database funziona solo per i database incorporati Se si desidera utilizzarlo per altri database, è necessario specificare spring.datasource.initialization-mode = always
Edu Costa,

6

Ecco come l'ho ottenuto:

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

}

Grazie all'autore di questo articolo:

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


Questo non funziona se si utilizza il servizio e se il servizio nel repository
autowiring

5

Puoi semplicemente creare un import.sqlfile src/main/resourcese Hibernate lo eseguirà quando viene creato lo schema.


4

Ho risolto un problema simile in questo modo:

@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

Se qualcuno fa fatica a farlo funzionare anche seguendo la risposta accettata , per me lavoro solo aggiungendo nei miei dettagli 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:

1

è possibile registrarsi e listener di eventi per raggiungere questo obiettivo come di seguito:

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

Quando viene attivato ContextRefreshEvent, otteniamo l'accesso a tutti i bean autowired nell'applicazione, inclusi modelli e repository.


1

Se si desidera inserire solo poche righe e si dispone dell'installazione JPA. Puoi usare qui sotto

    @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
Stavo usando questo lungo dorso, non ero in grado di ricordare. Questo è. Grazie.
Libero professionista

0

Questo funzionerà anche.

    @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

La soluzione più compatta (per dati dinamici) inserisce la soluzione @ mathias-dpunkt in MainApp (con 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

Ci sei quasi!

@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

Puoi usare il codice qui sotto. Nel codice seguente si verifica un inserimento del database durante l'avvio dell'applicazione di avvio a molla.

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.