当前位置: 移动技术网 > IT编程>开发语言>Java > SpringBoot项目启动时自动执行多个SQL脚本

SpringBoot项目启动时自动执行多个SQL脚本

2020年08月10日  | 移动技术网IT编程  | 我要评论
目录背景解决方案注意补充说明背景有一个项目fyk-config,该项目需要在配置的时候,需要创建一个配置表(FYK_PROPERTIES),并且向该表中插入各个微服务的配置记录。解决方案在SpringBoot中,有一个DataSourceInitializer类,该类会在项目启动的时候,执行初始化脚本。具体代码如下:首先,在resources目录下,创建文件夹scritp/db,然后在db文件夹下,放入sql文件:然后,在项目中,写一个配置类:@Slf4j@Configurationpu



背景

有一个项目fyk-config,该项目需要在配置的时候,需要创建一个配置表(FYK_PROPERTIES),并且向该表中插入各个微服务的配置记录。

解决方案

在SpringBoot中,有一个DataSourceInitializer类,该类会在项目启动的时候,执行初始化脚本。具体代码如下:
首先,在resources目录下,创建文件夹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,classpath后加个星号,意思是连jar包中的符合该规则的文件,都可以获取到,因为该项目最终会打成jar包来运行,如果不使用这个星号,就会出现在开发的时,没有问题,等到了测试或生产环境,就会出现找不到sql文件问题。当然,这里的文件规则,需要自己定义好,不要扫到了本不应加载的其他包中的文件了。

补充说明

按照上述操作,应该就能完成所需要求了。
但是有以下问题也许需要关注:

  1. SQL脚本是有执行顺序的,例如,在我的项目中,我需要建一个表,然后像该表插入初始化数据,所以有两个脚本,一个是建表,一个是初始化数据插入(当然,如果你所有的SQL语句都在一个文件中,那就不存在这种问题)。那么就应该先执行建表语句,再执行初始化语句。
    给一个解决方案:所有脚本文件的命名规则:序号-表名-操作类型-备注.sql,这里的序号就表示了这些脚本文件的执行顺序。如下:
    在这里插入图片描述
  2. 这些脚本文件,在项目启动的时候,会执行。那么项目重启之后,又会再次执行,这个应该如何避免?
    这里给一个参考:首先为每一个脚本文件,都配置一个验证是否执行的SQL语句,然后在加载这些配置文件的时候,先执行下这个SQL语句,判断是否执行该脚本,代码大致如下:
    在application配置文件中,配置每隔文件对应的验证sql,这里我的每个sql最终都会返回0或者1,返回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(); } 

仅供参考!!!

本文地址:https://blog.csdn.net/fyk844645164/article/details/107893626

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网