Spring Boot - Laden der Anfangsdaten

179

Ich frage mich, wie ich die ersten Datenbankdaten am besten laden kann, bevor die Anwendung gestartet wird. Was ich suche, ist etwas, das meine H2-Datenbank mit Daten füllt.

Zum Beispiel habe ich ein Domänenmodell "Benutzer". Ich kann auf Benutzer zugreifen, indem ich zu / users gehe, aber anfangs befinden sich keine Benutzer in der Datenbank, sodass ich sie erstellen muss. Gibt es überhaupt eine Möglichkeit, die Datenbank automatisch mit Daten zu füllen?

Im Moment habe ich eine Bean, die vom Container instanziiert wird und Benutzer für mich erstellt.

Beispiel:

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

Aber ich bezweifle sehr, dass dies der beste Weg ist. Oder ist es?

Lithicas
quelle
4
Das wird funktionieren oder einfach Daten hinzufügen data.sqlund / oder schema.sqlinitiieren. All dies ist im Referenzhandbuch dokumentiert (das ich zum Lesen empfehle).
M. Deinum
Bitte markieren Sie die richtige Antwort, wenn dies Ihnen geholfen hat.
Wiedergeboren am
Hat jemand das zum Laufen gebracht? Ich kann das immer noch nicht zusammenstellen und bin mir nicht sicher, was mir hier fehlt. git.io/v5SWx
srini

Antworten:

294

Sie können einfach eine data.sql- Datei in Ihrem Ordner src / main / resources erstellen, die beim Start automatisch ausgeführt wird. In dieser Datei fügen Sie einfach einige Einfügeanweisungen hinzu, z.

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

In ähnlicher Weise können Sie auch eine schema.sql- Datei (oder schema-h2.sql) erstellen, um Ihr Schema zu erstellen:

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

Normalerweise sollten Sie dies nicht tun müssen, da Spring Boot Hibernate bereits so konfiguriert, dass Ihr Schema basierend auf Ihren Entitäten für eine In-Memory-Datenbank erstellt wird. Wenn Sie schema.sql wirklich verwenden möchten, müssen Sie diese Funktion deaktivieren, indem Sie sie zu Ihren application.properties hinzufügen:

spring.jpa.hibernate.ddl-auto=none

Weitere Informationen finden Sie in der Dokumentation zur Datenbankinitialisierung .


Wenn Sie Spring Boot 2 verwenden , funktioniert die Datenbankinitialisierung nur für eingebettete Datenbanken (H2, HSQLDB, ...). Wenn Sie es auch für andere Datenbanken verwenden möchten, müssen Sie die spring.datasource.initialization-modeEigenschaft ändern :

spring.datasource.initialization-mode=always

Wenn Sie mehrere Datenbankanbieter verwenden, können Sie Ihre Datei data-h2.sql oder data-mysql.sql benennen, je nachdem, welche Datenbankplattform Sie verwenden möchten.

Damit dies funktioniert, müssen Sie die spring.datasource.platformEigenschaft jedoch konfigurieren :

spring.datasource.platform=h2
g00glen00b
quelle
Vielen Dank an @ g00glen00b für das Aufräumen: "und es wird beim Start automatisch ausgeführt". Ich habe Fehler erhalten, als ich die Datei data.sql mithilfe der Option addScript (s) in die Konfiguration meiner Bean aufgenommen habe. Zu diesem Zeitpunkt war das Schema noch nicht erstellt worden.
Benjamin Slabbert
5
@nespapu Sie haben es jedoch falsch gemacht, die schema.sql/ data.sqlDateien werden ausgeführt, wenn es spring.datasource.initializeist true(was die Standardeinstellung ist). spring.jpa.hibernate.ddl-autokann verwendet werden, um Ihre Tabellen basierend auf Ihrer Entitätskonfiguration zu generieren, anstatt eine SQL-Datei zu verwenden. Dies ist in speicherinternen Datenbanken standardmäßig aktiviert. Aus diesem Grund habe ich den Hinweis in meine Antwort eingefügt und erklärt, dass Sie, wenn Sie eine In-Memory-Datenbank verwenden und die verwenden möchten, diese schema.sqldeaktivieren müssen, da spring.jpa.hibernate.ddl-autosonst beide versuchen, Ihre Tabelle zu erstellen.
g00glen00b
7
Wenn Sie den data-h2.sqlDateinamen für Ihre Anfangsdaten verwenden möchten , sollten Sie dies auch spring.datasource.platform=h2in Ihren Anwendungseigenschaften festlegen .
Jason Evans
1
Die Datei data.sql wird jedes Mal ausgeführt, wenn die Spring-Boot-Anwendung gestartet wird . Dies bedeutet, dass Insert-Anweisungen eine org.h2.jdbc.JdbcSQLExceptionAusnahme verursachen können, da die Daten bereits in der Datenbank vorhanden sind. Ich verwende eine eingebettete H2-Datenbank, aber das Problem bleibt gleich.
Igor
1
@ g00glen00b das ist leider alles andere als einfach, da die H2-Datenbank zum Beispiel Probleme hat MERGE INTO. Ich habe herausgefunden, dass es eine Möglichkeit gibt, dies mithilfe einer import.sql- Datei anstelle einer data.sql zu umgehen . Es erfordert spring.jpa.hibernate.ddl-autozu erstellen oder erstellen Drop . Wenn dann die Schemadatei erstellt wird (und / oder eine schema.sql ausgeführt wird), wird auch die import.sql ausgeführt. Dennoch: Es fühlt sich wie eine Problemumgehung an und nicht wie eine saubere Implementierung des Erstellens von Init-Daten.
Igor
82

Wenn ich nur einfache Testdaten einfügen möchte, implementiere ich oft a ApplicationRunner . Implementierungen dieser Schnittstelle werden beim Start der Anwendung ausgeführt und können beispielsweise ein automatisch verdrahtetes Repository zum Einfügen einiger Testdaten verwenden.

Ich denke, eine solche Implementierung wäre etwas expliziter als Ihre, da die Schnittstelle impliziert, dass Ihre Implementierung etwas enthält, das Sie direkt nach der Fertigstellung Ihrer Anwendung ausführen möchten.

Ihre Implementierung würde etw aussehen. so was:

@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"));
    }
}
Mathias Dpunkt
quelle
32

Als Vorschlag versuchen Sie Folgendes:

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

Option 2: Initialisieren Sie mit Schema- und Datenskripten

Voraussetzungen: in müssen application.propertiesSie Folgendes erwähnen:

spring.jpa.hibernate.ddl-auto=none(Andernfalls werden Skripte vom Ruhezustand ignoriert und das Projekt wird nach Klassen @Entityund / oder @Tablekommentierten Klassen durchsucht.)

Fügen Sie dann in Ihrer MyApplicationKlasse Folgendes ein:

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

Wo sich der scriptsOrdner unter dem resourcesOrdner befindet (IntelliJ Idea)

Hoffe es hilft jemandem

Wiedergeboren
quelle
3
Option 2 ist großartig, da sie explizit beweist, was passiert. Insbesondere bei mehreren Datenquellen kann es erforderlich sein, die DataSourceAutoConfiguration.class von Spring zu deaktivieren. In diesem Fall funktionieren alle anderen hier bereitgestellten data.sql- und schema.sql-Lösungen nicht mehr.
Kaicarno
1
Wenn Sie anfängliche Daten laden möchten, aber dennoch möchten, dass Hibernate die DDL erstellt, Sie jedoch über mehrere Datenquellen verfügen und diese manuell einrichten, ist es in diesem Fall besser, die DataSourceInitializer-Bean von Spring gemäß stackoverflow.com/a/23036217/3092830 zu deklarieren wie es von der @ PostConstruct-Ausgabe für Sie dauern wird.
Kaicarno
32

Sie können spring.datasource.datader application.propertiesListe der SQL-Dateien, die Sie ausführen möchten, eine Eigenschaft hinzufügen . So was:

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

Die SQL-Insert-Anweisungen in jeder dieser Dateien werden dann ausgeführt, damit Sie die Dinge sauber halten können.

Wenn Sie die Dateien in den Klassenpfad einfügen, werden src/main/resourcessie beispielsweise angewendet. Oder ersetzen Sie classpath:durch file:und verwenden Sie einen absoluten Pfad zur Datei

Robjwilkins
quelle
5
Wenn Sie eine externe Datei möchten, vergessen Sie nicht, file:stattdessen zu setzen classpath:.
Aleksander Lech
Wo sollen sich die Dateien (accounts.sql, ...) befinden?
dpelisek
1
@dpelisek src / main / resources sollte funktionieren. Antwort aktualisiert.
Robjwilkins
14

Sie können so etwas verwenden:

@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
quelle
11

Mit Spring Boot können Sie ein einfaches Skript verwenden, um Ihre Datenbank mithilfe von Spring Batch zu initialisieren .

Wenn Sie jedoch etwas ausgefeilteres zum Verwalten von DB-Versionen usw. verwenden möchten, lässt sich Spring Boot gut in Flyway integrieren .

Siehe auch:

Xtreme Biker
quelle
6
Der Vorschlag, hier eine Frühlingscharge zu finden, scheint übertrieben.
Nick
@Nick, das OP erwähnt die Datenmenge nicht. Wie auch immer, die Antwort dreht sich nicht nur um Spring Batch.
Xtreme Biker
Meiner Meinung nach ist Flyway oder Liquibase der richtige Weg. Ich bin mir nicht sicher über den Kommentar von Nick und darüber hinaus über die positiven Stimmen von / src / main / resources. Ja, letzteres würde für kleine Projekte funktionieren. Die Antwort von Xtreme Biker gibt mit sehr geringem Aufwand so viel mehr Funktionalität.
Alexandros
10

In Spring Boot 2 funktionierte data.sql bei mir nicht wie in Spring Boot 1.5

import.sql

Darüber hinaus import.sqlwird beim Start eine Datei mit dem Namen im Stammverzeichnis des Klassenpfads ausgeführt, wenn Hibernate das Schema von Grund auf neu erstellt (dh wenn die Eigenschaft ddl-auto auf create oder create-drop festgelegt ist).

Beachten Sie, dass es sehr wichtig ist, wenn Sie Schlüssel einfügen, die nicht dupliziert werden können. Verwenden Sie nicht die Eigenschaft ddl-auto, die aktualisiert werden soll, da bei jedem Neustart dieselben Daten erneut eingefügt werden

Weitere Informationen finden Sie auf der Spring-Website

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

Ismail
quelle
In Spring 2 funktioniert die Datenbankinitialisierung nur für eingebettete Datenbanken. Wenn Sie sie für andere Datenbanken verwenden möchten, müssen Sie spring.datasource.initialization-mode = always angeben
Edu Costa
6

So habe ich das verstanden:

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

}

Vielen Dank an den Autor dieses Artikels:

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

adkl
quelle
Dies funktioniert nicht, wenn Sie Service verwenden und wenn Service im Autowiring-Repository
Silentsudo
5

Sie können einfach eine import.sqlDatei in erstellen src/main/resourcesund Hibernate führt sie aus, wenn das Schema erstellt wird.

Francesco Papagno
quelle
4

Ich habe ein ähnliches Problem folgendermaßen gelöst:

@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();
    }
}
Eine Tasche
quelle
1

Wenn jemand Schwierigkeiten hat, dies auch nach der akzeptierten Antwort zum Laufen zu bringen, füge ich nur src/test/resources/application.ymldie H2- datasourceDetails hinzu:

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

Sie können sich registrieren und einen Ereignis-Listener einrichten, um dies wie folgt zu erreichen:

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

Wenn das ContextRefreshEvent ausgelöst wird, erhalten wir Zugriff auf alle automatisch verdrahteten Beans in der Anwendung - einschließlich Modelle und Repositorys.

Ahmed Ahmed
quelle
1

Wenn Sie nur wenige Zeilen einfügen möchten und über ein JPA-Setup verfügen. Sie können unten verwenden

    @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

     }
Niraj Sonawane
quelle
1
Ich benutzte diesen langen Rücken, konnte mich nicht erinnern. Das ist es. Vielen Dank.
Freiberufler
0

Dies wird auch funktionieren.

    @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)));
        };
    }
Udara SS Liyanage
quelle
0

Die kompakteste (für dynamische Daten) Lösung von @ mathias-dpunkt in MainApp (mit 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()));
  }
}
Grigory Kislin
quelle
0

Du bist fast da!

@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"));
    }
}
Emmanuel Osimosu
quelle
0

Sie können den folgenden Code verwenden. Im folgenden Code erfolgt beim Starten der Spring-Boot-Anwendung eine Datenbankeinfügung.

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