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 
相关推荐
qq_4330994042 分钟前
Ubuntu20.04从零安装IsaacSim/IsaacLab
数据库
Dlwyz44 分钟前
redis-击穿、穿透、雪崩
数据库·redis·缓存
工业甲酰苯胺3 小时前
Redis性能优化的18招
数据库·redis·性能优化
冰帝海岸3 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象3 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了4 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
i道i4 小时前
MySQL win安装 和 pymysql使用示例
数据库·mysql
小怪兽ysl4 小时前
【PostgreSQL使用pg_filedump工具解析数据文件以恢复数据】
数据库·postgresql
qq_17448285755 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
wqq_9922502775 小时前
springboot基于微信小程序的食堂预约点餐系统
数据库·微信小程序·小程序