programing

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

newstyles 2023. 4. 4. 21:09

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

애플리케이션을 시작하기 전에 초기 데이터베이스 데이터를 로드하는 가장 좋은 방법은 무엇입니까?제가 찾고 있는 것은 H2 데이터베이스를 데이터로 채우는 것입니다.

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

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

하지만 나는 그것이 그것을 하는 최선의 방법인지 매우 의심스럽다.아니면 그러한가?

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 boot에서 메모리 데이터베이스에 대한 엔티티에 따라 스키마를 작성하도록 Hibernate가 이미 설정되어 있기 때문에 이 작업을 수행할 필요가 없습니다.스키마를 정말로 사용하고 싶은 경우.sql application.properties에 이 기능을 추가하여 비활성화해야 합니다.

spring.jpa.hibernate.ddl-auto=none

데이터베이스 초기화에 대한 자세한 내용은 설명서를 참조하십시오.


Spring Boot 2를 사용하는 경우 데이터베이스 초기화는 임베디드 데이터베이스(H2, HSQLDB 등)에서만 작동합니다.다른 데이터베이스에도 사용하려면 초기화 모드 속성을 변경해야 합니다.

spring.sql.init.mode=always # Spring Boot >=v2.5.0
spring.datasource.initialization-mode=always # Spring Boot <v2.5.0

여러 데이터베이스 벤더를 사용하는 경우 사용하는 데이터베이스 플랫폼에 따라 파일 이름을 data-h2.sql 또는 data-mysql.sql로 지정할 수 있습니다.

이를 수행하려면 데이터 소스 플랫폼 속성을 구성해야 합니다.

spring.sql.init.platform=h2 # Spring Boot >=v2.5.0
spring.datasource.platform=h2 # Spring Boot <v2.5.0

간단한 테스트 데이터만 삽입하는 경우, 저는 종종 를 구현합니다.이 인터페이스의 구현은 애플리케이션 부팅 시 실행되며 자동 배선 저장소를 사용하여 테스트 데이터를 삽입할 수 있습니다.

이러한 실장은 어플리케이션이 준비되고 나서 바로 실행할 수 있는 것이 인터페이스에 포함되어 있기 때문에 고객님의 실장보다 조금 더 명확하다고 생각합니다.

구현은 다음과 같습니다.

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

해서 '어울리지 않다'를 수 있어요.spring.datasource.data을 property property로 설정합니다.application.propertiesSQL을 사용합니다.다음과 같이 합니다.

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

//or (depending on SB version)

spring.sql.init.data-locations=classpath:accounts.sql, classpath:books.sql, file:reviews.sql

그런 다음 각 파일의 sql insert 문이 실행되어 깔끔한 상태를 유지할 수 있습니다.

classpath에 다음과 같이 .src/main/resources하다. 치환하다.classpath:file:합니다.

DDL 유형 SQL을 실행하려면 다음을 사용하십시오.

spring.datasource.schema=classpath:create_account_table.sql

// depending on spring version

spring.sql.init.schema-locations=classpath:create_account_table.sql 

편집: 이러한 솔루션은 신속한 도입과 가동을 가능하게 하는데 매우 유용하지만, 실제 가동에 적합한 솔루션이라면 플라이웨이(flyway)나 액상화(liquibase) 등의 프레임워크를 검토할 필요가 있습니다.이러한 프레임워크는 스프링과 잘 통합되며, 스키마 및 스탠딩 데이터를 빠르고 일관성 있는 버전 제어 방식으로 초기화할 수 있습니다.

이를 실현하는 방법은 여러 가지가 있습니다.다음 옵션 중 하나를 사용하는 것이 좋습니다.

옵션 1: 초기 설정CommandLineRunner 추가:

@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: 스키마 및 데이터 SQL 스크립트를 사용한 초기화

전제 조건:

application.properties

spring.jpa.hibernate.ddl-auto=none

설명:

ddl-auto for SQL이 실행됩니다. 프로젝트 검색@Entity "/"/"@Table주석 클래스

그 에서는 your네 then then then then?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폴더는 아래쪽에 있습니다.resourcesIdea)

도움이 되었으면 좋겠다

업데이트 04-2021: 두 가지 옵션을 모두 스프링 프로파일과 조합하면 추가 설정 파일을 작성할 필요가 없어 개발자로서의 생활이 쉬워집니다.

다음과 같은 것을 사용할 수 있습니다.

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

Spring Boot 2의 데이터.sql이 스프링 부트 1.5와 같이 작동하지 않았습니다.

import.sql

「」라고 하는 이름의 .import.sql클래스 경로의 루트는 시작 시 Hibernate가 스키마를 처음부터 작성한 경우(즉, ddl-auto 속성이 create 또는 create-drop로 설정된 경우) 실행됩니다.

키를 삽입할 수 없는 경우 키를 복제하지 마십시오.ddl-auto 속성은 다시 시작할 때마다 동일한 데이터가 다시 삽입되기 때문에 업데이트되도록 설정됩니다.

자세한 내용은 spring websit를 참조하십시오.

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

Spring Boot에서는 Spring Batch를 사용하여 간단한 스크립트를 사용하여 데이터베이스를 초기화할 수 있습니다.

그러나 DB 버전 등을 관리하기 위해 좀 더 정교한 것을 사용하고 싶다면 Spring Boot은 Flyway와 잘 통합됩니다.

다음 항목도 참조하십시오.

수 요.import.sqlsrc/main/resources스키마가 생성되면 휴지 상태가 실행됩니다.

여기에 와도 아무것도 효과가 없는 것 같으면, 그 변경에 의해 영향을 받고 있을 가능성이 있습니다.Spring Boot 2.5그 이후입니다.

다음은 postgresql에 사용하는 총 속성 세트입니다.

spring:
  sql.init.mode: always   <-----------------
  datasource:
    url: jdbc:postgresql://localhost:5432/products
    username: 
    password: 
  jpa:
    defer-datasource-initialization: true  <------------------
    hibernate:
      ddl-auto: create-drop   <----------------
    database-platform: org.hibernate.dialect.PostgreSQLDialect

제가 요.<---다음을 달성하기 위해 현재 주제에 대한 관련 속성.

  • ORM 벤더는 Java Entities 모델에서 사용자를 위해 데이터베이스 스키마를 작성합니다.
  • "에서 됩니다.data.sql

첫데이터와 함께 마세요.data.sql아래src/main/resources

참고: Spring Boot 2.5 릴리즈 노트

제가 얻은 방법은 다음과 같습니다.

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

비슷한 문제를 다음과 같이 해결했습니다.

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

거의 다 왔어!

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

다음과 같이 이벤트청취자를 등록할 수 있습니다.

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

ContextRefreshEvent가 실행되면 모델 및 저장소를 포함하여 애플리케이션 내의 모든 자동 전원 콩에 액세스할 수 있습니다.

만약 누군가가 받아들여진 답변에도 불구하고 이것을 작동시키는 데 어려움을 겪는다면, 나는 단지 내 의견을 추가하는 일만을 한다.src/test/resources/application.yml H2 thedatasource★★★★★★★★★★★★★★★★★★:

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

행을 몇 개만 삽입하고 JPA Setup을 사용하는 경우.아래를 사용할 수 있습니다.

    @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

     }

아래 코드를 사용할 수 있습니다.다음 코드에서는 스프링 부트애플리케이션의 기동시에 데이타베이스가 삽입됩니다.

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

}

이것도 잘 될 거예요.

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

콤팩트한 ( ) @mathias-dpunkt에 추가했습니다.@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()));
  }
}

URL을 사용하는 .JDBC 이 "JDBC URL"인지 합니다.jdbc:h2:mem:testdb

Spring Boot 어플리케이션에서 초기/데모 데이터 로딩이 용이한 라이브러리를 만들었습니다.https://github.com/piotrpolak/spring-boot-data-fixtures 에서 찾을 수 있습니다.

장치 패스에 있으면 됩니다.DICTIONARY기동 시의 이할 수 - 은, 「」를 실장하고 있는 입니다.DataFixture.

코드로 초기 데이터를 로드하는 것이 SQL 스크립트를 사용하여 로드하는 것보다 우수합니다.

  • 고정 장치의 로직은 애플리케이션 로직/도메인 모델에 가깝고 도메인이 진화함에 따라 리팩터링될 수 있습니다.
  • 증분 데모 데이터 업데이트의 이점 - 일부 사용자 데이터(애플리케이션 도입 후 손실될 필요가 없음)가 있는 QA 환경을 상상하면서 동시에 개발한 새로운 기능에 대한 데이터를 추가할 수 있습니다.

데이터 고정 예제:

/**
 * You can have as many fixture classes as you want.
 * @Order annotation is respected for the fixtures belonging to the same set.
 * You can make your demo database to be incrementally updated with fresh data
 * each time the application is redeployed - all you need to do is to write
 * a good condition in `canBeLoaded()` method.
 */
@Component
public class InitialDataFixture implements DataFixture {

    private final LanguageRepository languageRepository;

    // ...

    @Override
    public DataFixtureSet getSet() {
      return DataFixtureSet.DICTIONARY;
    }

    /**
     * We want to make sure the fixture is applied once and once only.
     * A more sophisticated condition can be used to create incremental demo data
     * over time without the need to reset the QA database (for example).
     */
    @Override
    public boolean canBeLoaded() {
      return languageRepository.size() == 0;
    }

    /**
     * The actual application of the fixture.
     * Assuming that data fixtures are registered as beans, this method can call
     * other services and/or repositories.
     */
    @Override
    public void load() {
      languageRepository.saveAll(Arrays.asList(
          new Language("en-US"), new Language("pl-PL")));
    }
}

이 개념은 Symfony Actrin Data Fixitions 번들에서 영감을 받았습니다.

MysqlDriver 를 사용하고 있는 경우는, @bean 주석의 Init 어트리뷰트를 사용해 보았습니다만, 동작합니다.

다음 경로에 스키마 및 데이터 SQL 파일을 생성한 후resources\Scripts

행을 에 추가합니다.application.properties

spring.jpa.hibernate.ddl-auto=none

응용 프로그램 내용을 편집합니다.

package com.spring_mvaen.demo;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
  @Override
  public void run(String... arg0) throws Exception {
    System.out.println("Hello world from Command Line Runner");
  }

  @Bean(name = "dataSource")
  public DriverManagerDataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/db_spring_rest?useUnicode=true&useLegacyDatetimeCode=fa    lse&serverTimezone=UTC&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true&useSSL=false");
    dataSource.setUsername("root");
    dataSource.setPassword("root");

    // schema init
    Resource initSchema = new ClassPathResource("scripts/schema.sql");
    Resource initData = new ClassPathResource("scripts/data.sql");
    DatabasePopulator databasePopulator = new ResourceDatabasePopulator(initSchema, initData);
    DatabasePopulatorUtils.execute(databasePopulator, dataSource);

    return dataSource;
  }


}

몇 가지 쿼리를 빠르게 삽입하려면 다음과 같이 할 수 있습니다.h2 data.sql쿼리도

application.properties다음을 포함합니다.

spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb

#This directs the data.sql file and help it to run
spring.sql.init.data-locations=classpath:data.sql
spring.jpa.defer-datasource-initialization=true

data.sql파일에는 다음이 포함됩니다.

INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10001, 'lighteducation', 'Learn dance', CURRENT_DATE ,false);

INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10002, 'lighteducation', 'Learn Angular14', CURRENT_DATE, false);

INSERT INTO todo (id, username, description, target_date, is_done) VALUES (10003, 'lighteducation', 'Learn Microservices', CURRENT_DATE,false);

추신:data.sql파일이 안에 있어야 합니다.src/main/resources

당신의.@Entity포함하다

@Getter
@Setter
@AllArgsConstructor
@ToString
@Entity
public class Todo {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
    private String description;
    private Date targetDate;
    private boolean isDone;

    protected Todo() {

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Todo todo = (Todo) o;
        return id == todo.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

기본적으로는 그게 다예요.메모리 내에 있습니다.즉, 어플리케이션을 재기동하면 데이터가 인식되어 쿼리에 표시되는 것과 같은 상태가 됩니다.

하지만 빠른 확인은 쉽다.

또, 를 사용해 패스에 액세스 할 수도 있습니다.http://localhost:8080/h2-console/또는 경로를 편집할 수 있습니다..properties파일

언급URL : https://stackoverflow.com/questions/38040572/spring-boot-loading-initial-data