springboot + mybatis启动时执行sql脚本

article/2025/9/14 10:37:39

目录

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

 2. 创建HdVersion对象

 3. 创建执行sql的dao

4. 创建dao对应的xml

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

6. 结语


背景:项目开发或发布阶段修改表结构,项目更新时需要手动执行脚本,需要优化为项目启动时自动检查版本并执行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_versionwhere 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{@Overridepublic 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方法内容如下:

@Overridepublic 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 {@AutowiredHdCommonDao hdCommonDao;private List<SchemaData> schema = new ArrayList<>();@Overridepublic 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 ) BEGINDECLARERows1 INT;SET Rows1 = 0;SELECTCOUNT(*) INTO Rows1FROMINFORMATION_SCHEMA.COLUMNSWHEREtable_schema = DATABASE ()AND upper( table_name )= TableNameAND upper( column_name )= ColumnName;IF( CType = 1 AND Rows1 <= 0 ) THENSET SqlStr := CONCAT( 'ALTER TABLE ', TableName, ' ADD COLUMN ', ColumnName, ' ', SqlStr );ELSEIF ( CType = 2 AND Rows1 > 0 ) THENSET SqlStr := CONCAT( 'ALTER TABLE ', TableName, ' MODIFY  ', ColumnName, ' ', SqlStr );ELSEIF ( CType = 3 AND Rows1 > 0 ) THENSET SqlStr := CONCAT( 'ALTER TABLE  ', TableName, ' DROP COLUMN  ', ColumnName );ELSESET SqlStr := '';END IF;IF( SqlStr <> '' ) THENSET @SQL1 = SqlStr;PREPARE stmt1FROM@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 )) BEGINDECLARERows1 int;DECLARESqlStr VARCHAR(4000);DECLAREtarget_database VARCHAR ( 100 );SELECT DATABASE() INTO target_database;SET Rows1 = 0;SELECTCOUNT(*) INTO Rows1FROMinformation_schema.statisticsWHEREtable_schema = DATABASE ()AND upper( table_name )= upper(TableName)AND upper( index_name )= upper(idx_name);IF Rows1<=0 THENSET SqlStr := CONCAT( 'alter table ', TableName, ' ADD INDEX ', idx_name, '(', ColumnNames, ') USING ', idx_type );END IF;IF( SqlStr <> '' ) THENSET @SQL1 = SqlStr;PREPARE stmt1FROM@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');

http://chatgpt.dhexx.cn/article/GYxyYoEX.shtml

相关文章

SpringBoot启动自动执行sql脚本

在开发当中我们每次发布服务都需要手动执行脚本&#xff0c;然后重启服务&#xff0c;而SpringBoot有服务启动自动执行sql脚本的功能的&#xff0c;可以为我们省去手动执行脚本的这一步&#xff0c;只需要部署新的服务即可。 这个功能是SpringBoot自带的不需要引入额外的依赖&a…

Excel数据转化为sql脚本

在实际项目开发中&#xff0c;有时会遇到客户让我们把大量Excel数据导入数据库的情况。这时我们就可以通过将Excel数据转化为sql脚本来批量导入数据库。 1 在数据前插入一列单元格&#xff0c;用来拼写sql语句。 具体写法&#xff1a;"insert into t_student (id,name,ag…

MySQL导出sql脚本文件

⭐️前言⭐️ sql脚本文件在我们做项目时&#xff0c;特别是学习别人的开源项目时经常需要进行导入导出操作&#xff0c;才能在自己的系统上跑起来&#xff0c;这篇文章主要介绍如何导出sql脚本文件&#xff0c;具体操作如下&#xff0c;附带截图详解。 &#x1f349;博客主页…

dataGrip导出sql脚本

1.打开dataGrip。 2.选择要导出的数据库表。 3.点击右键->选择"Dump Data to File(s)", 同时选择&#xff0c;Skip Computed Columns(sql),Add Table Definition(sql),Overwrite Exsting Files和Single File。 4.点击sql Inserts 5.选择文件保存位置 6.生成sql脚…

linux下plsql怎么执行sql脚本,plsql怎么执行sql脚本

首先,我们需要登录需要执行sql文件的用户,在我们确保sql文件无误的情况下,进入plsqldeveloper: 1,找到tools---》import tables ---》选择sql insert,不要选中sqlplus,选择最下面的那个导入sql文件,选中好sql文件后,点击import就会执行sql语句,生成日志。 2,如果执行…

DBeaver执行SQL脚本文件

1、右键库名&#xff0c;点击工具-->执行脚本 2、在弹出窗口中选择输入文件&#xff0c;并修改Extra command args:--default-character-setutf8&#xff0c;防止中文乱码&#xff0c;点击开始按钮。 3、执行完成。

kettle执行SQL脚本

参考一下kettle官方文档 kettle什么时候需要创建临时表呢 SELECT * WHERE cid IN(xxx) 的数据太多&#xff0c;占了很大内存。 目标表有没有必要做逻辑删除&#xff0c;如果做逻辑删除&#xff0c;后期数据量增长过快。 目标表增量更新&#xff1a;1、sql直接插入&#xff1…

SQL Server SQL脚本

本节的主要内容是要教大家怎么通过编写 SQL 脚本来查询、更新并且运行数据库。 利用 SQL 脚本我们能做很多事情&#xff0c;比如插入数据、读取数据、更新数据以及删除数据等&#xff1b;它们也可以用于创建数据库对象&#xff0c;如表&#xff0c;视图&#xff0c;存储过程&a…

SQL Server 数据库之生成与执行 SQL 脚本

生成与执行 SQL 脚本 1. 将数据库生成2. 将数据表生成 SQL 脚本4. 执行 SQL 脚本 1. 将数据库生成 使用对象资源管理器能快速创建整个数据的脚本&#xff0c;也能使用默认选项创建单个数据库对象的脚本&#xff1b; 用户能在查询编辑器窗口中对文件或剪贴板创建脚本&#xff0…

SQL 常用脚本大全

1、行转列的用法PIVOT CREATE table test (id int,name nvarchar(20),quarter int,number int) insert into test values(1,N苹果,1,1000) insert into test values(1,N苹果,2,2000) insert into test values(1,N苹果,3,4000) insert into test values(1,N苹果,4,5000) insert …

打砖游戏,详解每一行代码,历经三个小时解析,初学可看

打转游戏详解版 网上只要搜一下“打砖游戏”&#xff0c;基本会看到很多一样的代码&#xff0c;主要是注释也很少&#xff0c;对于python不熟悉的人来说&#xff0c;根本看不懂&#xff0c;只会拿来运行着玩玩。 于是我历经三个小时&#xff0c;把代码几乎每一行都注释了一遍&…

今天开始敲代码

新手小白今天开始自学敲代码了呜呜呜 加油加油加油

开发12年,整整6百万行代码,史上最烂的开发项目长这样

程序员&#xff08;ID&#xff1a;imkuqin&#xff09;猿妹编译 原文&#xff1a;https://projectfailures.wordpress.com 最近有个史称世界上最烂的开发项目在朋友圈刷屏&#xff0c;这个项目到底有多烂呢&#xff1f; 这个项目拖了整整12年&#xff0c;造出6百万行代码&#…

“低代码”抢走程序员的饭碗?没有的事

编者按&#xff1a;眼下发展势头正猛的低代码&#xff0c;承受着两种截然不同的声音。一种声音觉得低代码是“减负”神器&#xff0c;可以帮助减轻开发工作量&#xff0c;大幅提升开发效率&#xff1b;另一种声音却觉得低代码非“善类”&#xff08;主要是程序员群体&#xff0…

程序是如何跑起来的

一个简单的11?的问题对于我们而言就是脱口而出&#xff0c;而对于计算机却不是这样。 那么当我们用从c语言或者其他高级语言来写的时候计算机是如何工作运算加法&#xff08;其他原理类似&#xff09;的呢&#xff1f; 当c语言由编写到运行程序需要经过 1.预处理 2.编译 …

据说程序员写完代码是这个样子,99%的人都中枪了

点击上方“程序员大咖”&#xff0c;选择“置顶公众号” 关键时刻&#xff0c;第一时间送达&#xff01; 1、当你改错一行代码的时候 2、当你想要重构别人的代码时 3、当你尝试想要修复一个bug时的样子 4、当你凌晨4点还在工作时的样子 5、星期五下午项目经理给你分配任务时的样…

祖传代码千万别动

IT程序猿 微博网友评论&#xff1a; 洞侠X&#xff1a;祖传 爱因斯坦老道士&#xff1a;这是傻啊 砸自己脚下的 前端折腾小能手&#xff1a;嗯 sunnysab&#xff1a;想要改进代码的时候 精彩回顾 ♡ 程序员究竟能干多少年&#xff1f; ♡ 互联网公司各岗位真实工作内容起底 ♡ …

泡着枸杞写bug的三流程序员凭什么逆袭到一线大厂?

大多数互联网的从业者都有一个梦想&#xff1a;进大厂。 因为不仅可以享受较好的福利待遇&#xff0c;也能与更优秀的人一起共事&#xff0c;获得更专业、更快速的成长。 最近经常有朋友提及想要入门编程学习&#xff0c;该如何学习&#xff1f; 关于编程学习&#xff0c;各…

刚进公司就把祖上十八代单传的代码优化了是什么体验?

刚进公司就把祖传代码优化了,让公司的APP稳定性提高一半,大幅提升了用户体验是什么体验? 一个字!爽! 这不比斗破里面萧炎“三年之期”打脸嫣然来的爽?不比博人传燃?爽文小说都不敢这么写。 关键,没人信啊。 扮猪吃老虎的大佬 去年金三银四帮公司面试的时候认识了…

百行代码手撸扫雷(下)c/c++

在上一篇文章中已经带大家如何搭建开发环境了 需求分析 这里是一个8*8的地图 地雷随机分布数字表示周围一共有多少颗雷 数字1表示红色区域内有1课雷,2表示有两颗 如何显示数字 右上角没有如何判断呢?行加1或列加一不就越界了吗,所以为了帮面条件判断我们把8行8列改成10行1…