Spring Boot - Đang tải dữ liệu ban đầu


179

Tôi đang tự hỏi cách tốt nhất để tải dữ liệu cơ sở dữ liệu ban đầu trước khi ứng dụng bắt đầu là gì? Những gì tôi đang tìm kiếm là thứ gì đó sẽ lấp đầy cơ sở dữ liệu H2 của tôi với dữ liệu.

Ví dụ: tôi có một mô hình miền "Người dùng" Tôi có thể truy cập người dùng bằng cách truy cập / người dùng nhưng ban đầu sẽ không có bất kỳ người dùng nào trong cơ sở dữ liệu nên tôi phải tạo chúng. Có cách nào để điền vào cơ sở dữ liệu với dữ liệu tự động?

Hiện tại tôi có một Bean được khởi tạo bởi container và tạo người dùng cho tôi.

Thí dụ:

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

Nhưng tôi rất nghi ngờ đó là cách tốt nhất để làm điều đó. Hoặc là nó?


4
Điều đó sẽ hoạt động, hoặc đơn giản là thêm data.sqlvà / hoặc schema.sqlvào dữ liệu init .. Tất cả điều này được ghi lại trong hướng dẫn tham khảo (mà tôi đề nghị nên đọc).
M. Deinum

Vui lòng đánh dấu câu trả lời đúng nếu điều đó giúp bạn.
Tái sinh

Có ai có cái này để làm việc không? Tôi vẫn không thể kết hợp điều này với nhau và không chắc chắn những gì tôi đang thiếu ở đây. git.io/v5SWx
srini

Câu trả lời:


294

Bạn có thể chỉ cần tạo một tệp data.sql trong thư mục src / main / resource của mình và nó sẽ được thực hiện tự động khi khởi động. Trong tệp này, bạn chỉ cần thêm một số câu lệnh chèn, ví dụ:

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

Tương tự như vậy, bạn có thể tạo một schema.sql tệp (hoặc schema-h2.sql) cũng như để tạo ra sơ đồ của bạn:

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

Mặc dù thông thường, bạn không cần phải làm điều này vì Spring boot đã cấu hình Hibernate để tạo lược đồ của bạn dựa trên các thực thể của bạn cho một cơ sở dữ liệu trong bộ nhớ. Nếu bạn thực sự muốn sử dụng lược đồ. Bạn sẽ phải tắt tính năng này bằng cách thêm tính năng này vào ứng dụng của bạn.

spring.jpa.hibernate.ddl-auto=none

Thông tin thêm có thể được tìm thấy tại tài liệu về khởi tạo cơ sở dữ liệu .


Nếu bạn đang sử dụng Spring boot 2 , khởi tạo cơ sở dữ liệu chỉ hoạt động đối với cơ sở dữ liệu nhúng (H2, HSQLDB, ...). Nếu bạn muốn sử dụng nó cho các cơ sở dữ liệu khác, bạn cần thay đổi thuộc spring.datasource.initialization-modetính:

spring.datasource.initialization-mode=always

Nếu bạn đang sử dụng nhiều nhà cung cấp cơ sở dữ liệu, bạn có thể đặt tên tệp dữ liệu của mình-h2.sql hoặc data-mysql.sql tùy thuộc vào nền tảng cơ sở dữ liệu nào bạn muốn sử dụng.

Để thực hiện công việc đó, bạn sẽ phải định cấu hình thuộc spring.datasource.platformtính:

spring.datasource.platform=h2

Cảm ơn bạn @ g00glen00b vì đã xóa: "và nó sẽ được thực hiện tự động khi khởi động". Tôi đã gặp lỗi khi bao gồm tệp data.sql trong cấu hình bean của tôi bằng tùy chọn addScript (s). Tại thời điểm này, lược đồ chưa được xây dựng.
Benjamin Slabbert

5
@nespapu Bạn có nó sai, mặc dù các schema.sql/ data.sqlfile sẽ được thực hiện khi spring.datasource.initializetrue(đó là mặc định). spring.jpa.hibernate.ddl-autocó thể được sử dụng để tạo các bảng của bạn dựa trên cấu hình thực thể của bạn thay vì sử dụng tệp SQL. Điều này theo mặc định được kích hoạt trên cơ sở dữ liệu trong bộ nhớ. Đó là lý do tại sao tôi đã thêm ghi chú vào câu trả lời của mình, giải thích rằng nếu bạn sử dụng cơ sở dữ liệu trong bộ nhớ và bạn muốn sử dụng schema.sql, bạn cần phải vô hiệu hóa spring.jpa.hibernate.ddl-autonếu không cả hai sẽ cố gắng tạo bảng của bạn.
g00glen00b

7
Nếu bạn muốn sử dụng data-h2.sqltên tệp cho dữ liệu ban đầu của mình, bạn cũng nên đặt spring.datasource.platform=h2trong thuộc tính ứng dụng của mình.
Jason Evans

1
Tệp data.sql được thực thi mỗi khi ứng dụng khởi động lò xo được kích hoạt. Điều này có nghĩa là nếu bạn có các câu lệnh chèn, chúng có thể gây ra org.h2.jdbc.JdbcSQLExceptionngoại lệ, vì dữ liệu đã có trong cơ sở dữ liệu. Tôi đang sử dụng một cơ sở dữ liệu H2 nhúng, nhưng vấn đề vẫn giữ nguyên.
Igor

1
@ g00glen00b thật đáng buồn, tất cả đều dễ dàng, vì cơ sở dữ liệu H2 chẳng hạn MERGE INTO. Tôi đã tìm ra rằng, có một cách để phá vỡ điều này bằng cách sử dụng tệp import.sql thay vì data.sql . Nó đòi hỏi spring.jpa.hibernate.ddl-autophải tạo hoặc tạo-thả . Sau đó, bất cứ khi nào file schema được tạo ra (và / hoặc một schema.sql được thực hiện), các import.sql được thực hiện là tốt. Tuy nhiên: nó cảm thấy giống như một cách giải quyết và không phải là một triển khai rõ ràng trong việc tạo dữ liệu init.
Igor

82

Nếu tôi chỉ muốn chèn dữ liệu thử nghiệm đơn giản, tôi thường thực hiện a ApplicationRunner. Việc triển khai giao diện này được chạy khi khởi động ứng dụng và có thể sử dụng ví dụ như kho lưu trữ tự động để chèn một số dữ liệu thử nghiệm.

Tôi nghĩ việc triển khai như vậy sẽ rõ ràng hơn một chút so với giao diện của bạn bởi vì giao diện ngụ ý rằng việc triển khai của bạn có chứa thứ gì đó bạn muốn làm trực tiếp sau khi ứng dụng của bạn sẵn sàng.

Việc thực hiện của bạn sẽ trông sth. như thế này:

@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

Theo gợi ý, hãy thử điều này:

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

Tùy chọn 2: Khởi tạo với lược đồ và tập lệnh dữ liệu

Điều kiện tiên quyết: trong application.propertiesbạn phải đề cập đến điều này:

spring.jpa.hibernate.ddl-auto=none(nếu không các tập lệnh sẽ bị bỏ qua bởi chế độ ngủ đông và nó sẽ quét dự án cho @Entityvà / hoặc @Tablecác lớp chú thích)

Sau đó, trong MyApplicationlớp của bạn dán này:

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

Nơi scriptsthư mục được đặt trong resourcesthư mục (IntelliJ Idea)

Hy vọng nó sẽ giúp được ai đó


3
Tùy chọn 2 là tuyệt vời vì nó cung cấp bằng chứng rõ ràng về những gì đang xảy ra. Với nhiều nguồn dữ liệu đặc biệt, có thể cần phải vô hiệu hóa DataSourceAutoConfiguration. Class của Spring trong trường hợp tất cả các giải pháp data.sql và lược đồ được cung cấp ở đây ngừng hoạt động.
komsarno

1
Nếu bạn muốn tải dữ liệu ban đầu nhưng vẫn muốn Hibernate tạo DDL nhưng bạn có nhiều nguồn dữ liệu và thiết lập chúng theo cách thủ công, thì một lựa chọn tốt hơn trong trường hợp này là khai báo bean DataSourceInitializer của Spring như trên stackoverflow.com/a/23036217/3092830 vì nó sẽ giải quyết vấn đề @PostConstruct cho bạn.
komsarno

32

Bạn có thể thêm một thuộc spring.datasource.datatính để application.propertiesliệt kê các tệp sql bạn muốn chạy. Như thế này:

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

Các câu lệnh chèn sql trong mỗi tệp này sau đó sẽ được chạy, cho phép bạn giữ mọi thứ gọn gàng.

Nếu bạn đặt các tệp trong đường dẫn lớp, ví dụ như src/main/resourceschúng sẽ được áp dụng. Hoặc thay thế classpath:bằng file:và sử dụng một đường dẫn tuyệt đối đến tệp


5
trong trường hợp bạn muốn một tập tin bên ngoài đừng quên đặt file:thay thế classpath:.
Aleksander Lech

các tệp sẽ được đặt ở đâu?
dpelisek

1
@dpelisek src / main / resource nên hoạt động. Trả lời cập nhật.
robjwilkins

14

Bạn có thể sử dụng một cái gì đó như thế này:

@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 cho phép bạn sử dụng một tập lệnh đơn giản để khởi tạo cơ sở dữ liệu của mình, sử dụng Spring Batch .

Tuy nhiên, nếu bạn muốn sử dụng một cái gì đó chi tiết hơn một chút để quản lý các phiên bản DB, v.v., Spring Boot tích hợp tốt với Flyway .

Xem thêm:


6
đề nghị lô mùa xuân ở đây có vẻ quá mức cần thiết.
Nick

@Nick, OP không đề cập đến lượng dữ liệu .. Dù sao câu trả lời không phải là tất cả về đợt xuân.
Xtreme Biker

Theo ý kiến ​​của tôi, Flyway hoặc Liquibase là cách chính xác để đi. Không chắc chắn về nhận xét của Nick và hơn thế nữa về các upvote của / src / main / resource. Vâng, sau này sẽ làm việc cho các dự án nhỏ. Câu trả lời của Xtreme Biker mang lại thông qua nỗ lực rất nhỏ nên nhiều chức năng hơn.
Alexandros

10

Trong Spring Boot 2 data.sql không hoạt động với tôi như trong spring boot 1.5

nhập khẩu

Ngoài ra, một tệp có tên import.sqltrong thư mục gốc của classpath được thực thi khi khởi động nếu Hibernate tạo lược đồ từ đầu (nghĩa là, nếu thuộc tính ddl-auto được đặt thành tạo hoặc tạo-thả).

Lưu ý rất quan trọng nếu bạn chèn Khóa không thể trùng lặp, không sử dụng thuộc tính ddl-auto được đặt thành cập nhật vì với mỗi lần khởi động lại sẽ chèn lại cùng một dữ liệu

Để biết thêm thông tin bạn tham gia websit mùa xuân

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


Trong khởi tạo cơ sở dữ liệu Spring 2 chỉ hoạt động cho cơ sở dữ liệu nhúng Nếu bạn muốn sử dụng nó cho các cơ sở dữ liệu khác, bạn cần chỉ định spring.datasource.initialization-mode = always
Edu Costa

6

Đây là cách tôi có được rằng:

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

}

Cảm ơn tác giả của bài viết này:

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


Điều này không hoạt động nếu bạn đang sử dụng dịch vụ và nếu dịch vụ trong kho lưu trữ tự động
silentsudo

5

Bạn có thể chỉ cần tạo một import.sqltệp trong src/main/resourcesvà Hibernate sẽ thực thi nó khi lược đồ được tạo.


4

Tôi đã giải quyết vấn đề tương tự theo cách này:

@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

Nếu ai đó đang vật lộn để làm cho nó hoạt động ngay cả khi làm theo câu trả lời được chấp nhận , đối với tôi chỉ làm việc thêm vào chi tiết src/test/resources/application.ymlH2 của tôi datasource:

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

1

bạn có thể đăng ký và nghe sự kiện để đạt được điều đó như dưới đây:

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

Khi ContextRefreshEvent được kích hoạt, chúng tôi có quyền truy cập vào tất cả các loại đậu tự động trong ứng dụng - bao gồm các mô hình và kho lưu trữ.


1

Nếu bạn muốn chèn chỉ vài hàng và bạn có Cài đặt JPA. Bạn có thể sử dụng dưới đây

    @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
Tôi đã sử dụng cái lưng dài này, không thể nhớ lại. Đây là nó. cảm ơn.
Freelancer

0

Điều này cũng sẽ làm việc.

    @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

Giải pháp nhỏ gọn nhất (cho dữ liệu động) đưa giải pháp @ mathias-dpunkt vào MainApp (với 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

Bạn đã gần tới!

@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

Bạn có thể sử dụng mã dưới đây. Trong đoạn mã sau đây, việc chèn cơ sở dữ liệu xảy ra trong quá trình khởi động ứng dụng khởi động mùa xuân.

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

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.