Spring容器初始化之前,利用SpringBoot监听器做一些数据库方面的检查

之所以把数据库的连接、结构、最小初始化等检查项放到SpringBoot监听器而不是Spring容器的初始过程。是有原因的。

1:SpringBoot监听器是SpringBoot初始化过程中,最先被执行的那一批周期函数。

2:数据库连接测试能很快的获得结果。

3:Spring容器初始化时,里面的ApplicationListener,ApplicationRunner,初始化方法等有部分实现是异步预热缓存数据。如果不前置检查数据表结构,很容易报sql异常。当产品有更新表结构时,项目发布时,很容易出现该问题。

java 复制代码
public class PrepareTableCheckListener implements SpringApplicationRunListener {

    private static List<String> MYSQL_PRODUCT_NAMES = new ArrayList<>();

    static {
        MYSQL_PRODUCT_NAMES.add("kingbase8");
        MYSQL_PRODUCT_NAMES.add("mysql");
    }

    public PrepareTableCheckApplicationRunListener(SpringApplication application, String[] args) {
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

        ConfigurableEnvironment environment = context.getEnvironment();
        String jdbcDriver = environment.getProperty("spring.datasource.driver-class-name");
        String jdbcUrl = environment.getProperty("spring.datasource.url");
        String jdbcUserName = environment.getProperty("spring.datasource.username");
        String jdbcUserPassword = environment.getProperty("spring.datasource.password");
        String testNetworkSQL = environment.getProperty("spring.datasource.hikari.connection-test-query");

        if (null == jdbcUrl || null == jdbcUserPassword || null == jdbcUserName) {
            return;
        }

        loadDriverClass(jdbcDriver);

        // 数据库连接检查
        DriverManager.setLoginTimeout(2);
        try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUserName, jdbcUserPassword)
        ) {
            Statement statement = conn.createStatement();
            statement.execute(testNetworkSQL);
        } catch (Exception e) {
            log.warn("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+");
            log.warn("...................★");
            log.warn("..................▍..★");
            log.warn("..................▍.一 .☆");
            log.warn("................. ▍ ..帆. ★");
            log.warn("..................▍ ... 风. ☆");
            log.warn("..................▍ ... ..顺. ★");
            log.warn("................. ▍.万 事 如 意. ☆");
            log.warn("..................▍☆ .★ .☆ .★. ☆");
            log.warn("..................▍");
            log.warn("..▍∵ ☆ ★...▍▍....█▍ ☆ ★∵▍..");
            log.warn("◥█▅▅██▅▅██▅▅▅▅▅███◤");
            log.warn(".◥███████████████◤");
            log.warn("~~~~◥█████████████◤~~~~");
            log.warn("~~~~~~~~~~~~~~~~~~~~~~~~~");
            log.warn("~~~哦豁,连不上数据库,哈哈哈哈哈~~~~~~~");
            log.warn("~~~~~~~~~~~~~~~~~~~~~~~~~");
            log.warn("+jdbcDriver={}", jdbcDriver);
            log.warn("+jdbcUrl={}", jdbcUrl);
            log.warn("+jdbcUserName={}", jdbcUserName);
            log.warn("+jdbcUserPassword={}", jdbcUserPassword);
            log.warn("+testNetworkSQL={}", testNetworkSQL);
            log.warn("+exception class =" + e.getClass().getName());
            log.warn("+exception message =" + e.getMessage());
            log.warn("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+");
            System.exit(0);
        }


		// 预定义表结构检查
        Set<String> prepareSqls;

        String urlLower = jdbcUrl.toLowerCase();
        if (ZYListUtils.anyMatch(MYSQL_PRODUCT_NAMES, urlLower::contains)) {
            prepareSqls = readSql("db_prepare_mysql");
        } else {
            prepareSqls = readSql("db_prepare_oracle");
        }

        if (prepareSqls.isEmpty()) {
            return;
        }
        
        try (
                Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUserName, jdbcUserPassword)
        ) {
            conn.setAutoCommit(true);
            Statement stmt = conn.createStatement();
            for (String prepareSql : prepareSqls) {
                try {
                    stmt.executeUpdate(prepareSql);
                } catch (Exception e) {
                    log.warn("execute table check sql 【{}】 false ,the column or table may exit!", prepareSql);
                }
            }
        } catch (Exception e) {
            log.warn("execute table check sql  false ,the Connection get false!");
        }

    }

    public Set<String> readSql(String dir) {
        Set<String> sqls = new HashSet<>();

        Resource[] resources;
        try {
            resources = new PathMatchingResourcePatternResolver().getResources("classpath*:/" + dir + "/**/*.sql");
        } catch (IOException e) {
            return sqls;
        }
        if (resources.length == 0) {
            return sqls;
        }
        for (Resource resource : resources) {
            try (InputStream inputStream = resource.getInputStream()) {
                String sqlText = IoUtil.read(inputStream, Charset.defaultCharset());
                String[] sqlArr = sqlText.split(";");
                for (String sql : sqlArr) {
                    List<String> sqlItems = new ArrayList<>();
                    StringTokenizer stringTokenizer = new StringTokenizer(sql);
                    while (stringTokenizer.hasMoreTokens()) {
                        String item = stringTokenizer.nextToken();
                        sqlItems.add(item);
                    }
                    String finalSql = ZYStrUtils.join(sqlItems, " ");
                    sqls.add(finalSql);
                }
            } catch (Exception e) {
                log.warn("sql read false");
            }
        }

        return sqls;
    }

    private void loadDriverClass(String jdbcDriver) {
        try {
            Class.forName(jdbcDriver);
        } catch (ClassNotFoundException ex) {
            throw new LocalException("数据库驱动文件不存在,请检查是否缺少驱动包或spring.datasource.driver-class-name配置不正确!");
        }
    }
}

spring.factories配置

java 复制代码
org.springframework.boot.SpringApplicationRunListener =xxx.framework.mybatis.PrepareTableCheckListener 
相关推荐
消失在人海中14 分钟前
oracle 游标的管理
数据库·oracle
盖世英雄酱5813620 分钟前
小小的改动,竟然效率提高了1000倍
数据库·后端
韩zj20 分钟前
springboot调用python文件,python文件使用其他dat文件,适配windows和linux,以及docker环境的方案
windows·spring boot·python
遇见你真好。24 分钟前
flowable任务监听器和java-service依赖注入问题
spring·springboot·flowable
八股文领域大手子26 分钟前
从接口400ms到20ms,记录一次JVM、MySQL、Redis的混合双打
jvm·数据库·redis·mysql·jar
提高记忆力1 小时前
truncate,drop,delete分析
数据库
堕落年代1 小时前
SpringBoot使用Redisson时候进行Redis事务回滚
spring boot·redis·后端
江节胜-胜行全栈AI2 小时前
PostgreSQL-中文字段排序-修改字段的排序规则
数据库·postgresql
就是蠢啊2 小时前
SpringBoot 基础知识,HTTP 概述
数据库·mysql·spring