SpringBoot 프로젝트가 시작될 때 여러 SQL 스크립트를 자동으로 실행합니다.

배경

구성할 때 구성 테이블(FYK_PROPERTIES)을 생성하고 각 마이크로서비스의 구성 기록을 테이블에 삽입해야 하는 fyk-config 프로젝트가 있습니다.

해결책

SpringBoot에는 프로젝트가 시작될 때 초기화 스크립트를 실행하는 DataSourceInitializer 클래스 가 있습니다 . 구체적인 코드는 다음과 같습니다.
먼저 리소스 디렉터리에 scritp/db 폴더를 생성한 다음 db 폴더에 sql 파일을 넣습니다.
여기에 이미지 설명을 삽입하세요
그런 다음 프로젝트에서 구성 클래스를 작성합니다.

@Slf4j
@Configuration
public class DbScriptInit {
    
    

    @Bean
    public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) throws IOException {
    
    
        final DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource);
        initializer.setDatabasePopulator(this.databasePopulator());
        return initializer;
    }

    /**
     * 初始化数据资源
     * 
     * @author FYK
     * @return org.springframework.core.io.Resource[] 资源对象
     */
    private Resource[] getResources() throws IOException {
    
    
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:script/db/*.sql");
        log.info("加载初始化脚本文件---------start");
        for (Resource resource : resources) {
    
    
            log.info(resource.getFilename());
        }
        log.info("加载初始化脚本文件---------end");
        return resources;
    }

    /**
     * 初始化数据策略
     * 
     * @author FYK
     * @return org.springframework.jdbc.datasource.init.DatabasePopulator 策略对象
     */
    private DatabasePopulator databasePopulator() throws IOException {
    
    
        final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScripts(this.getResources());
        return populator;
    }
}

알아채다

설명할 점은 두 가지입니다.

  1. db 폴더 아래에 여러 스크립트가 있을 수 있으므로 여기서는 PathMatchingResourcePatternResolver를 사용하여 여러 .sql 파일을 읽어야 합니다.
  2. 파일을 얻을 수 있는 경로는 classpath*:script/db/*.sql 이고, 클래스패스 뒤에 별표가 추가되는데, 이는 프로젝트가 결국에는 규칙을 만족하는 jar 패키지의 파일이라도 얻을 수 있다는 것을 의미합니다. jar 패키지로 패키징하여 실행하려면 이 별표를 사용하지 않으면 개발 중에 나타나게 됩니다. 문제 없습니다. 테스트 또는 프로덕션 환경에서 기다리면 sql 파일을 찾을 수 없는 문제가 발생합니다 . 물론, 로드해서는 안 되는 다른 패키지의 파일을 검사하지 않도록 여기에서 파일 규칙을 직접 정의해야 합니다.

보주

위의 작업에 따라 필수 요구 사항을 완료할 수 있어야 합니다.
그러나 다음 문제에는 주의가 필요할 수 있습니다.

  1. SQL 스크립트는 순차적으로 실행되는데, 예를 들어 제 프로젝트에서는 테이블을 구축한 후 테이블에 초기화 데이터를 삽입해야 하므로 테이블을 구축하는 스크립트와 초기화 데이터를 삽입하는 스크립트가 2개가 있습니다. (물론, 모든 SQL 문이 하나의 파일에 있으면 그러한 문제가 없습니다). 그런 다음 테이블 생성 문을 먼저 실행한 다음 초기화 문을 실행해야 합니다.
    해결 방법: 모든 스크립트 파일의 명명 규칙: 일련 번호-테이블 이름-작업 유형-remarks.sql, 여기서 일련 번호는 이러한 스크립트 파일의 실행 순서를 나타냅니다. 다음과 같이:
    여기에 이미지 설명을 삽입하세요
  2. 이 스크립트 파일은 프로젝트가 시작될 때 실행됩니다. 그런 다음 프로젝트가 다시 시작된 후 다시 실행됩니다. 이를 방지하려면 어떻게 해야 합니까?
    참고 사항은 다음과 같습니다: 먼저 각 스크립트 파일에 대해 SQL 문을 구성하여 실행 여부를 확인한 다음 이러한 구성 파일을 로드할 때 먼저 SQL 문을 실행하여 스크립트 실행 여부를 결정합니다.코드는 대략 다음과 같습니다. : in
    application 구성 파일에 각 파일에 해당하는 검증 sql을 구성하는데, 여기서 my sql 각각은 결국 0 또는 1을 반환하게 된다. 실행:
fyk.db-script.check-sql={\
  "1-FYK_PROPERTIES-DQL":"select case when exists(select 1 from all_tables t where t.TABLE_NAME = upper('fyk_properties')) then 1 else 0 end as result from dual",\
  "2-FYK_PROPERTIES-DML-fyk-oauth":"select case when exists(select 1 from fyk_properties t where t.application='fyk-oauth') then 1 else 0 end as result from dual"\
  }

그런 다음 DbScriptInit 클래스의 이미지를 수정하고 내부의 getResources 메서드를 수정합니다.

    @Value("#{${fyk.db-script.check-sql}}")
    private Map<String, String> checkSql;

    @Autowired
    private JdbcTemplate jdbcTemplate;   
     
   /**
     * 初始化数据资源
     * <p>
     * 首先,每个脚本文件,都要对应一个验证sql,只有验证SQL返回0的时候,才执行该脚本文件,否则不执行。
     * </p>
     * 
     * @author FYK
     * @return org.springframework.core.io.Resource[] 资源对象
     */
    private Resource[] getResources() throws IOException {
    
    
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:script/db/*.sql");
        List<Resource> resultList = new LinkedList<>();
        log.info("加载初始化脚本文件---------start");
        String checkSqlStr;
        Integer resutlNum;
        String dqlSqlMatch = "";
        for (Resource resource : resources) {
    
    
            String fileFullName = resource.getFilename();
            // 如果DQL语句都没有执行,则默认要执行DML语句
            if (fileFullName.matches(".*DML.*") && StringUtil.isNotBlank(dqlSqlMatch)
                && fileFullName.matches(dqlSqlMatch)) {
    
    
                resultList.add(resource);
                continue;
            }
            // 获得验证脚本
            checkSqlStr = this.getCheckSql(fileFullName);
            if (StringUtil.isNotBlank(checkSqlStr)) {
    
    
                resutlNum = jdbcTemplate.queryForObject(checkSqlStr, Integer.class);
                if (resutlNum != null && resutlNum == 0) {
    
    
                    resultList.add(resource);
                    if (fileFullName.matches(".*DQL.*")) {
    
    
                        dqlSqlMatch += this.buildDqlSqlMatch(fileFullName, dqlSqlMatch);
                    }
                } else {
    
    
                    log.info("sql初始化脚本文件[{}]验证结果为1,跳过该脚本", fileFullName);
                }
            }
        }
        log.info("加载初始化脚本文件---------end");
        return resultList.toArray(new Resource[0]);
    }

    /**
     * 获取去掉后缀的文件名
     * 
     * @author FYK
     * @param fileFullName
     *            资源文件全名
     * @return java.lang.String 文件名
     */
    private String getCheckSql(@NonNull String fileFullName) {
    
    
        String fileName = fileFullName.replace(".sql", "");
        log.info("{}.sql", fileName);
        String checkSqlStr = checkSql.get(fileName);
        if (StringUtil.isBlank(checkSqlStr)) {
    
    
            log.warn("sql脚本文件[{}.sql]的执行验证语句未配置~~~~~不执行该SQL脚本", fileName);
        }
        return checkSqlStr;
    }

    /**
     * 构建DQL语句匹配规则
     * 
     * @author FYK
     * @param fileFullName
     *            全文件名
     * @param dqlSqlMatch
     *            DQL语句匹配规则
     * @return java.lang.String 构建结果
     */
    private String buildDqlSqlMatch(String fileFullName, String dqlSqlMatch) {
    
    
        String[] fileFullNameSplit = fileFullName.split("-");
        StringBuilder sb = new StringBuilder();
        if (StringUtil.isBlank(dqlSqlMatch)) {
    
    
            sb.append(".*\\-");
        } else {
    
    
            sb.append("|.*\\-");
        }
        sb.append(fileFullNameSplit[1]);
        sb.append("\\-.*");
        return sb.toString();
    }

참고용으로만! ! !

추천

출처blog.csdn.net/fyk844645164/article/details/107893626