IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot + mybatis启动时执行sql脚本 -> 正文阅读

[Java知识库]springboot + mybatis启动时执行sql脚本

背景:项目开发或发布阶段修改表结构,项目更新时需要手动执行脚本,需要优化为项目启动时自动检查版本并执行sql语句。

开发环境:jdk1.8

开发工具:IDEA

框架:springboot+mybatisplus

数据库:mysql 5.7

SpringBoot本身提供了丰富的组件供开发者调用,本次优化通过ApplicationRunner类实现。

在SpringBoot中,提供了一个接口:ApplicationRunner
该接口中,只有一个run方法,他执行的时机是:spring容器启动完成之后,就会紧接着执行这个接口实现类的run方法。

mybaits默认不能批量执行sql,yml配置文件中连接数据库url配置添加以下参数:

allowMultiQueries=true

如:

url: jdbc:mysql://192.168.100.xx:3306/xxx?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8

让我们开始吧!

1. 创建数据版本表,结构如下:

CREATE TABLE `hd_version` (
  `id` varchar(64) NOT NULL,
  `version` varchar(64) DEFAULT NULL COMMENT '版本号',
  `created` datetime DEFAULT NULL COMMENT '创建时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据版本';

?2. 创建HdVersion对象

@Data
public class HdVersionEntity {

	private String id;
	private String version;
	private String remark;
	private Date created;

}

?3. 创建执行sql的dao

@Mapper
public interface HdCommonDao  {

    //查询版本号是否存在
    int selectVersion(@Param("version") String version);

    //查询版本表是否存在
    int selectTableExist(@Param("tableName") String tableName);

    //新增版本
    int insertVersion(HdVersionEntity entity);

    //执行sql
    @Update("${sql}")
    void updateSql(@Param("sql") String sql);

}

4. 创建dao对应的xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.hdkj.hdiot.configure.dao.HdCommonDao">

    <select id="selectVersion" resultType="int">
        selecT count(1) from hd_version
        where version = #{version}
    </select>
    <select id="selectTableExist" resultType="int">
        select count(*) count  from information_schema.TABLES where TABLE_NAME = #{tableName} and  table_schema = (select  database())

    </select>

    <insert id="insertVersion">
        insert into hd_version(id,version, remark, created) values (uuid(),#{version}, #{remark}, #{created})
    </insert>


</mapper>

5.创建sql执行器,实现ApplicationRunner

@Order(1)
@Component
@Slf4j
public class HdSchemaExecutor implements ApplicationRunner{

	@Override
	public void run(ApplicationArguments applicationArguments) throws Exception {
		//do something
	}

}

? 约定一个存放sql文件的目录:

sql文件命名规则不进行约束

HdSchemaExecutor?新建一个全局变量,存放脚本列表:

private List<SchemaData> schema = new ArrayList<>();

SchemaData对象如下:


@Data
public class SchemaData {
    /**
     * 版本号
     */
    public String version;

    /**
     * 文件名
     */
    public String fileName;

    public SchemaData(String version, String fileName) {
        this.version = version;
        this.fileName = fileName;
    }
}

HdSchemaExecutor?新增方法,给schema赋值:

public void buildSchemas(){
        schema.add(new SchemaData("v2.1","schema_v2.1.sql"));
        schema.add(new SchemaData("v2.2","schema_v2.2.sql"));
        schema.add(new SchemaData("v2.3","schema_v2.3.sql"));
    }

run方法内容如下:


    @Override
    public void run(ApplicationArguments args) throws Exception {
        //初始版本列表
        buildSchemas();
        //定义sql文件路径
        String basePath = "schemas/";
        //非版本控制,初始化脚本
        ClassLoader loader = this.getClass().getClassLoader();
        //通过流的方式获取项目路径下的文件
        InputStream inputStream = loader.getResourceAsStream(basePath + "init.sql");
        //获取文件内容
        String sql = IoUtil.readUtf8(inputStream);
        try {
            //判断版本表是否存在
            int count = hdCommonDao.selectTableExist("hd_version");
            if (count == 0) {
                hdCommonDao.updateSql(sql);
            }
            for (SchemaData schemaData : schema) {
                //查询版本记录是否存在
                count = hdCommonDao.selectVersion(schemaData.getVersion());
                if (count == 0) {
                    log.info("--------------执行数据脚本,版本:" + schemaData.getVersion());
                    //获取对应sql脚本
                    inputStream = loader.getResourceAsStream(basePath + schemaData.getFileName());
                    sql = IoUtil.readUtf8(inputStream);
                    hdCommonDao.updateSql(sql);
                    HdVersionEntity entity = new HdVersionEntity();
                    entity.setId(UUID.randomUUID().toString());
                    entity.setVersion(schemaData.getVersion());
                    entity.setCreated(new Date());
                    entity.setRemark(schemaData.getFileName());
                    //写入版本记录
                    hdCommonDao.insertVersion(entity);
                }
            }

        } catch (IORuntimeException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            inputStream.close();
        }
    }

完整代码如下:

package com.hdkj.hdiot.configure.config;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import com.hdkj.hdiot.configure.common.SchemaData;
import com.hdkj.hdiot.configure.dao.HdCommonDao;
import com.hdkj.hdiot.configure.entity.HdVersionEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author: 刘成辉
 * @Date: 2022/7/27 8:45
 * @Description:
 */
@Order(1)
@Component
@Slf4j
public class HdSchemaExecutor implements ApplicationRunner {

    @Autowired
    HdCommonDao hdCommonDao;

    private List<SchemaData> schema = new ArrayList<>();

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //初始版本列表
        buildSchemas();
        //定义sql文件路径
        String basePath = "schemas/";
        //非版本控制,初始化脚本
        ClassLoader loader = this.getClass().getClassLoader();
        //通过流的方式获取项目路径下的文件
        InputStream inputStream = loader.getResourceAsStream(basePath + "init.sql");
        //获取文件内容
        String sql = IoUtil.readUtf8(inputStream);
        try {
            //判断版本表是否存在
            int count = hdCommonDao.selectTableExist("hd_version");
            if (count == 0) {
                hdCommonDao.updateSql(sql);
            }
            for (SchemaData schemaData : schema) {
                //查询版本记录是否存在
                count = hdCommonDao.selectVersion(schemaData.getVersion());
                if (count == 0) {
                    log.info("--------------执行数据脚本,版本:" + schemaData.getVersion());
                    //获取对应sql脚本
                    inputStream = loader.getResourceAsStream(basePath + schemaData.getFileName());
                    sql = IoUtil.readUtf8(inputStream);
                    hdCommonDao.updateSql(sql);
                    HdVersionEntity entity = new HdVersionEntity();
                    entity.setId(UUID.randomUUID().toString());
                    entity.setVersion(schemaData.getVersion());
                    entity.setCreated(new Date());
                    entity.setRemark(schemaData.getFileName());
                    //写入版本记录
                    hdCommonDao.insertVersion(entity);
                }
            }

        } catch (IORuntimeException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            inputStream.close();
        }
    }

    public void buildSchemas() {
        schema.add(new SchemaData("v2.1", "schema_v2.1.sql"));
        schema.add(new SchemaData("v2.2", "schema_v2.2.sql"));
        schema.add(new SchemaData("v2.3", "schema_v2.3.sql"));
    }
}

6. 结语

? ? ? ?每次发布版本是放脚本到对应目录下,初始化方法中新增版本对应关系

附初始化脚本文件:


/*==============================================================*/
/* Table: hd_version                                            */
/*==============================================================*/
create table if not exists hd_version
(
   id                   varchar(64) not null,
   version              varchar(64) comment '版本号',
   created              datetime comment '创建时间',
   remark               varchar(500) comment '备注',
   primary key (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据版本';

/* 创建函数Pro_Temp_ColumnWork操作表字段 */

DROP PROCEDURE IF EXISTS Pro_Temp_ColumnWork;
CREATE  PROCEDURE `Pro_Temp_ColumnWork` ( TableName VARCHAR ( 50 ), ColumnName VARCHAR ( 50 ), SqlStr VARCHAR ( 4000 ), CType INT ) BEGIN
    DECLARE
        Rows1 INT;

    SET Rows1 = 0;
    SELECT
        COUNT(*) INTO Rows1
    FROM
        INFORMATION_SCHEMA.COLUMNS
    WHERE
            table_schema = DATABASE ()
      AND upper( table_name )= TableName
      AND upper( column_name )= ColumnName;
    IF
        ( CType = 1 AND Rows1 <= 0 ) THEN

        SET SqlStr := CONCAT( 'ALTER TABLE ', TableName, ' ADD COLUMN ', ColumnName, ' ', SqlStr );

    ELSEIF ( CType = 2 AND Rows1 > 0 ) THEN

        SET SqlStr := CONCAT( 'ALTER TABLE ', TableName, ' MODIFY  ', ColumnName, ' ', SqlStr );

    ELSEIF ( CType = 3 AND Rows1 > 0 ) THEN

        SET SqlStr := CONCAT( 'ALTER TABLE  ', TableName, ' DROP COLUMN  ', ColumnName );
    ELSE
        SET SqlStr := '';

    END IF;
    IF
        ( SqlStr <> '' ) THEN

        SET @SQL1 = SqlStr;
        PREPARE stmt1
            FROM
            @SQL1;
        EXECUTE stmt1;

    END IF;

END;
/** 函数创建结束 **/

/*创建定义普通索引函数*/
DROP PROCEDURE IF EXISTS Modify_index;

CREATE PROCEDURE Modify_index (
    TableName VARCHAR ( 50 ),
    ColumnNames VARCHAR ( 500 ),
    idx_name VARCHAR ( 50 ),
    idx_type VARCHAR ( 50 )) BEGIN
    DECLARE
        Rows1 int;
    DECLARE
        SqlStr VARCHAR(4000);
    DECLARE
        target_database VARCHAR ( 100 );
    SELECT DATABASE
               () INTO target_database;

    SET Rows1 = 0;
    SELECT
        COUNT(*) INTO Rows1
    FROM
        information_schema.statistics
    WHERE
            table_schema = DATABASE ()
      AND upper( table_name )= upper(TableName)
      AND upper( index_name )= upper(idx_name);
    IF Rows1<=0 THEN
        SET SqlStr := CONCAT( 'alter table ', TableName, ' ADD INDEX ', idx_name, '(', ColumnNames, ') USING ', idx_type );
    END IF;
    IF
        ( SqlStr <> '' ) THEN

        SET @SQL1 = SqlStr;
        PREPARE stmt1
            FROM
            @SQL1;
        EXECUTE stmt1;

    END IF;

END;
/*创建定义普通索引函数结束*/
Pro_Temp_ColumnWork:维护表字段

exp:

CALL Pro_Temp_ColumnWork ('table_name','column_name','int(1) ', 1);
Modify_index:维护表索引

exp:

call Modify_index('table_name','column_names','idx_tid_target','BTREE');
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:29:56  更:2022-08-06 10:31:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:02:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码