스프링 부트-초기 데이터로드


180

응용 프로그램이 시작되기 전에 초기 데이터베이스 데이터를로드하는 가장 좋은 방법이 무엇인지 궁금합니다. 내가 찾고있는 것은 H2 데이터베이스를 데이터로 채울 무언가입니다.

예를 들어, / users로 이동하여 사용자에게 액세스 할 수있는 도메인 모델 "User"가 있지만 처음에는 데이터베이스에 사용자가 없으므로 사용자를 만들어야합니다. 어쨌든 데이터베이스를 자동으로 데이터로 채울 수 있습니까?

현재 컨테이너에 의해 인스턴스화되고 사용자를 생성하는 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 년

도움이 되었으면 정답을 표시하십시오.
Reborn

누구든지 이것을 작동 시켰습니까? 나는 아직도 이것을 함께 넣을 수 없으며 내가 여기서 무엇을 놓치고 있는지 잘 모르겠습니다. git.io/v5SWx
srini

답변:


295

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

정리해 주셔서 감사합니다 : "시작시 자동으로 실행됩니다". addScript (s) 옵션을 사용하여 bean의 구성에 data.sql 파일을 포함 시키면 오류가 발생했습니다. 이 시점에서 스키마는 아직 빌드되지 않았습니다.
Benjamin Slabbert

5
@nespapu 당신이 잘못했지만 schema.sql/ data.sql파일은 spring.datasource.initializeis true(기본값) 일 때 실행됩니다 . spring.jpa.hibernate.ddl-autoSQL 파일을 사용하지 않고 엔티티 구성을 기반으로 테이블을 생성하는 데 사용할 수 있습니다. 기본적으로 메모리 내 데이터베이스에서 활성화됩니다. 그렇기 때문에 메모리에 데이터베이스를 사용하고을 사용하려는 경우 schema.sql비활성화해야하며 spring.jpa.hibernate.ddl-auto그렇지 않으면 둘 다 테이블을 만들려고한다고 설명합니다.
g00glen00b

7
data-h2.sql초기 데이터에 파일 이름 을 사용하려면 spring.datasource.platform=h2응용 프로그램 속성에서도 설정해야 합니다.
Jason Evans

1
data.sql 파일은 스프링 부트 응용 프로그램이 실행될 때마다 실행 됩니다. 이는 삽입 명령문이있는 org.h2.jdbc.JdbcSQLException경우 데이터가 데이터베이스에 이미 있기 때문에 예외 가 발생할 수 있음을 의미 합니다. 임베디드 H2 데이터베이스를 사용하고 있지만 문제는 동일하게 유지됩니다.
Igor

1
@ g00glen00b 슬프게도 H2 데이터베이스에 문제가 있기 때문에 쉽지 MERGE INTO않습니다. 나는 data.sql 대신 import.sql 파일을 사용하여 이것을 우회하는 방법이 있다는 것을 알았습니다 . 작성 또는 작성 해야 합니다 . 그런 다음 스키마 파일이 작성 될 때 (및 / 또는 schema.sql 이 실행될 때마다 ) import.sql 도 실행됩니다. 여전히 : 그것은 해결 방법처럼 느껴지고 init 데이터를 깨끗하게 구현하지는 않습니다. spring.jpa.hibernate.ddl-auto
Igor

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(그렇지 않으면 스크립트는 최대 절전 모드에서 무시되며 프로젝트 @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 아이디어)

누군가에게 도움이되기를 바랍니다.


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 insert 문이 실행되어 깔끔하게 유지할 수 있습니다.

예를 들어 파일을 클래스 경로에 넣으면 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의 답변은 아주 작은 노력으로 훨씬 더 많은 기능을 제공합니다.
Alexandros

10

Spring Boot 2에서 data.sql은 spring boot 1.5에서와 같이 작동하지 않습니다.

import.sql

또한 파일 이름이 import.sql Hibernate가 스키마를 처음부터 생성하면 (즉, ddl-auto 속성이 create 또는 create-drop으로 설정되어있는 경우) 시작시 클래스 경로의 루트에 이 실행됩니다.

키를 복제 할 수없는 경우 매우 중요합니다. ddl-auto 속성을 사용하지 마십시오. 다시 시작할 때마다 동일한 데이터가 다시 삽입되므로 업데이트하도록 설정하십시오.

자세한 내용은 스프링 웹 사이트를 참고하십시오.

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


당신은 항상 spring.datasource.initialization 모드 = 지정하는 당신이 필요로하는 다른 데이터베이스에 사용하려는 경우 봄에이 데이터베이스 초기화는 임베디드 데이터베이스 작동
에듀 코스타

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/


이것은 당신이 서비스를 사용하는 경우 일을하지 않으며, 경우 저장소 autowiring에의 서비스
silentsudo

5

당신은 단순히 import.sql파일을 생성 할 수 src/main/resources있고 스키마가 생성 될 때 Hibernate가 그것을 실행할 것이다.


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

다음과 같이 달성하고 등록하기 위해 이벤트 리스너를 등록 할 수 있습니다.

@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
나는 이것을 오랫동안 사용하고 있었고, 기억할 수 없었다. 이거 야. 감사.
Freelancer

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 솔루션을 가장 컴팩트 한 (동적 데이터 용) @ Mainias (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.