手搓的一款基于sql脚本的数据初始化组件

产口孵化新项目时,表中需要提前预置部份数据。如字典,角色、菜单、配置等。以达到快速布署的目的。这部份数据会随着某些因素的影响。如地区变化、系统定制方面。无法使用统一的一份sql。旧方案是在代码里面动态的作insert操作。在项目启动的时候初始化这部份数据。但玩着玩着失控了。主要是看不见、摸不着,不晓得往库里加了些什么数据。烽烟四起,四处报警。在此基础上进行了改良。使用了sql脚本+占位符的模式。并引入了初始化脚本和增量脚本的功能。将脚本执行情况记录到一张表中,防止重复执行。
代码结构:
手搓的一款基于sql脚本的数据初始化组件_第1张图片

代码如下:


@Component
@Slf4j
@ConditionalOnProperty(
        prefix = GlobalConstant.CONFIG_PREFIX + "dependence", name = "enable-squirrel-execute", havingValue = "true")
public class SquirrelExecutor implements EasyApplicationRunner, SquirrelConstant {

    @Autowired
    private DataSource dataSource;
    @Autowired
    private AreaMapper areaMapper;
    @Autowired
    private DeployMapper deployMapper;
    @Autowired
    private SquirrelContextBuilder squirrelContextBuilder;
    @Autowired
    private MinioTemplate minioTemplate;

    @SneakyThrows
    @Override
    public void doBusiness() {
        SquirrelContext context = squirrelContextBuilder.buildSquirrelContext();
        if (context.isStop()) {
            return;
        }
        if (context.hasGlobalInit()) {
            // 执行初始化操作
            doAddScript(context);
        } else {
            // 做增量操作
            doInitScript(context);
        }

        // 记录执行次数
        recordExecuteTimes(context.getExecuteTimes());
    }

    public void doAddScript(SquirrelContext context) {
        // 增量脚本
        List<String> addFileNames = new ArrayList<>();
        Map<String, String> addScripts = context.getAddScripts();
        addScripts.forEach((fileName, script) -> {
            if (!context.hasExecute(fileName)) {
                executeScript(script, context);
                addFileNames.add(fileName);
            }
        });
        if (ZYListUtils.isNotEmptyList(addFileNames)) {
            List<Deploy> addSigns = ZYListUtils.list2list(addFileNames, Deploy::new);
            deployMapper.insertBatch(addSigns);
        }
    }

    public void doInitScript(SquirrelContext context) {
        // 从地区库中挑选出需要的地区保存地区
        List<Area> areas = context.getInitAreas();
        areaMapper.insertSplitBatch(areas, 200);

        List<String> initScripts = context.getInitScripts();
        for (String initScript : initScripts) {
            executeScript(initScript, context);
        }
        // 标记已初始化
        List<Deploy> signs = new ArrayList<>();
        Deploy signInitDeploy = new Deploy(INIT_KEY);
        signs.add(signInitDeploy);
        // 标记当前增量脚本已执行
        Map<String, String> addScripts = context.getAddScripts();
        addScripts.forEach((fileName, script) -> {
            Deploy signAddDeploy = new Deploy(fileName);
            signs.add(signAddDeploy);
        });

        // 导入附件
        // initAttachment();

        deployMapper.insertBatch(signs);
    }


    private void executeScript(String script, SquirrelContext context) {
        Reader reader = ScriptBuilder.of(script, context).buildExecuteReader();
        if (null == reader) {
            return;
        }
        try (Connection connection = dataSource.getConnection()) {
            // 建表
            ScriptRunner sr = new ScriptRunner(connection);
            sr.setAutoCommit(true);
            sr.setStopOnError(false);
            sr.setSendFullScript(true);
            sr.runScript(reader);
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("初始化系统表失败" + script);
        }
    }

    private void recordExecuteTimes(int hasExecuteTimes) {
        Deploy signTimes = new Deploy();
        signTimes.setConfigKey(InitParam.EXECUTE_TIMES);
        signTimes.setConfigValue(String.valueOf(hasExecuteTimes + 1));
        if (hasExecuteTimes > 0) {
            deployMapper.updateById(signTimes);
        } else {
            deployMapper.insert(signTimes);
        }
    }

    public void initAttachment() {
        SquirrelAttachment squirrelAttachment = new SquirrelAttachment();
        squirrelAttachment.findAttachment();
        if (squirrelAttachment.isEmpty()) {
            return;
        }
        squirrelAttachment.forEach((fileName, fileInput) -> {
            try (InputStream is = fileInput) {
                FileWrapper fileWrapper = new FileWrapper();
                fileWrapper.setNewFileName(fileName);
                fileWrapper.setExtName(ZYFileUtils.extName(fileName));
                minioTemplate.uploadInputStream(fileWrapper, is, MediaType.APPLICATION_OCTET_STREAM.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }


}
public class SquirrelContextBuilder {

    @Autowired
    private AreaRepositoryMapper areaRepositoryMapper;
    @Autowired
    private DeployMapper deployMapper;
    @Autowired
    private DependenceProperties dependenceProperties;

    public SquirrelContext buildSquirrelContext() {
        SquirrelContext context = new SquirrelContext();

        // 布署参数配置
        List<Deploy> deploys = deployMapper.selectList(Wrappers.query());
        InitParam initParam = new InitParam(deploys);
        context.setInitParam(initParam);

        Integer squirrelMaxExecuteTimes = dependenceProperties.getSquirrelMaxExecuteTimes();
        int hasExecuteTimes = initParam.hasExecuteTimes();
        // 为保证系统稳定,达到一次脚本初始化次数后将不会再执行。
        if (hasExecuteTimes > squirrelMaxExecuteTimes) {
            log.warn("已达脚本最大初始化次数");
            context.setStop(true);
            return context;
        }

        String rootAreaId = initParam.getRootAreaId();
        if (ZYStrUtils.isNull(rootAreaId)) {
            throw new LocalException("初始化地区根id不能为空");
        }

        AreaRepository rootArea = areaRepositoryMapper.get(rootAreaId);
        if (null == rootArea) {
            throw new LocalException("地区表尚未初始化");
        }

        // 地区层级
        int areaLevel = initParam.getAreaLevel();
        if (areaLevel == 0) {
            throw new LocalException("地区层级未配置");
        }

        // 查询子级地区
        List<AreaRepository> subAreas = findSubAreasByLevel(rootAreaId, rootArea, areaLevel);

        List<AreaRepository> areaContainer = findSubAreasByLevel(rootAreaId, rootArea, 6);
        Map<String, List<AreaRepository>> parentAreaContainer = new HashMap<>();
        for (AreaRepository parentArea : areaContainer) {
            String parentAreaId = parentArea.getId();
            if (ZYTreeUtils.isRootTree(parentAreaId)) {
                continue;
            }
            for (AreaRepository subArea : areaContainer) {
                String subAreaParentIdId = subArea.getParentId();
                if (subAreaParentIdId.equals(parentAreaId)) {
                    ZYMapUtils.flushMapList(parentAreaId, parentAreaContainer, subArea);
                }

            }
        }

        context.setScriptWrapper(new ScriptWrapper());
        context.setParentAreaContainer(parentAreaContainer);
        context.setRootArea(rootArea);
        context.setSubArea(subAreas);

        return context;
    }

    public List<AreaRepository> findSubAreasByLevel(String rootAreaId, AreaRepository rootArea, int areaLevel) {
        List<AreaRepository> subAreas = new ArrayList<>();
        subAreas.add(rootArea);
        List<String> parentIds = Collections.singletonList(rootAreaId);
        for (int i = 1; i < areaLevel; i++) {
            List<AreaRepository> subAreaItems = findAreaByParentIds(parentIds);
            if (ZYListUtils.isEmptyList(subAreaItems)) {
                break;
            }
            parentIds = ZYListUtils.collectField(subAreaItems, AreaRepository::getId);
            subAreas.addAll(subAreaItems);
        }
        return subAreas;
    }

    private List<AreaRepository> findAreaByParentIds(List<String> parentIds) {
        if (ZYListUtils.isEmptyList(parentIds)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<AreaRepository> wrapper = Wrappers.lambdaQuery();
        wrapper.in(AreaRepository::getParentId, parentIds);
        return areaRepositoryMapper.selectList(wrapper);
    }
}

public class ScriptWrapper implements SquirrelConstant {

    List<String> initScripts = new ArrayList<>();
    Map<String, String> addScripts = new HashMap<>();

    public ScriptWrapper() {
        Resource[] resources = null;
        try {
            resources = new PathMatchingResourcePatternResolver().getResources("xxx");
        } catch (IOException e) {
            log.warn("脚本文件读取失败");
        }
        if (null == resources) {
            return;
        }

        for (Resource resource : resources) {
            String filename = resource.getFilename();
            if (null == filename) {
                continue;
            }

            // 项目类型A的脚本
            if (filename.startsWith(a)) {
                if (!b) {
                    continue;
                }
            }
            // 项目类型B的脚本
            if (filename.startsWith(b)) {
                if (!b) {
                    continue;
                }
            }

            try (InputStream inputStream = resource.getInputStream()) {
                String sqlScript = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
                if (SquirrelConstant.isInitScript(filename)) {
                    initScripts.add(sqlScript);
                } else if (SquirrelConstant.isAddScript(filename)) {
                    addScripts.put(filename, sqlScript);
                }
            } catch (IOException e) {
                log.warn("脚本文件读取失败");
            }
        }
    }
}

squirrel是松鼠的意思是,该组件寓意把初始化脚本初始化多多弄好。项目多时就有粮可吃。不用加班。好像解释有点牵强。那就是乱起的。

你可能感兴趣的:(java)