Mybatis自动创建数据库表,并根据创建的表自动生成Mvc框架基础代码
背景
在集成MyBatis 的代码生成器的时候,发现该ORM框架并不能很好将Bean实体转换为数据库中的表。在网上搜索相关的案例也没有发现很好的解决方案。因此决定手搓一种实现方案,将工程目录下面的sql脚本文件通过Mybatis的ScriptRunner类中的runScript方法运行,然后将新建的表通过Mybatis的代码生成器,创建Mvc框架层基础的业务代码。
改文章只适用于单集群单服务节点实例的运行。多实例分布式下,需要引入分布式锁,提高数据的一致性和正确性。
Bean映射成数据表的方式
-
Hibernate/JPA:可以通过 hibernate.hbm2ddl.auto 配置,根据实体类自动生成或更新表结构。(因为项目并为使用Hibernate/JPA,因此在本文章并为体现)
-
Flyway/Liquibase: 两种主流的数据库版本控制工具,用于管理数据库变更(DDL 和 DML)。它们的核心功能是追踪、版本化并有序执行 SQL 脚本,确保团队协作和环境间的数据库结构一致性。(考虑到工程不想,引如过多外部依赖,因此并不此文章体现,读者可以根据实际情况具体实现)
-
Mybatis实现方式: 通过ScriptRunner执行sql创建脚本。并通过代码逻辑实现版本的控制。
三者的比较
实现原理&方式
工程目录结构
核心代码
- 创建sql版本记录表
CREATE TABLE IF NOT EXISTS sql_version (`id` varchar(64) NOT NULL,`sql_path` varchar(128) NULL COMMENT 'sql执行路径' UNIQUE,`status` bit(1) NULL COMMENT 'sql执行状态',PRIMARY KEY (`id`));
其中 sql_path 代码,sql文件在resources文件下面的路径,status 代表改版本sql文件的执行情况,未运行态sql文件为0 运行态的sql为1.
- sql版本插入语句
## 插入语句要带上 ON DUPLICATE KEY UPDATE 保障数据存在时 不在更具数据库中记录
insert into sql_version(id, sql_path, status)
values (UUID(),"sql\\0713\\0713.sql", 0)ON DUPLICATE KEY UPDATE sql_path = sql_path;
- sql脚本执行器
package oauth2.config;import jakarta.annotation.PostConstruct;
import oauth2.dao.SqlVersionMapper;
import oauth2.model.SqlVersion;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;@Component
public class SqlScriptExecutor {private final DataSource dataSource;private final ResourceLoader resourceLoader;@Autowiredprivate SqlVersionMapper sqlVersionMapper;public SqlScriptExecutor(DataSource dataSource, ResourceLoader resourceLoader) {this.dataSource = dataSource;this.resourceLoader = resourceLoader;}@PostConstructpublic void init() throws SQLException, IOException {executeScript("sql\\version.sql");List<SqlVersion> sqlVersionList = sqlVersionMapper.selectAllSqlVersionsAndStatusIsFalse();for (SqlVersion sqlVersion : sqlVersionList) {executeScript(sqlVersion.getSqlPath());sqlVersion.setStatus(true);sqlVersionMapper.updateById(sqlVersion);}}public void executeScript(String scriptPath) throws IOException, SQLException {try (Connection conn = dataSource.getConnection()) {ScriptRunner runner = new ScriptRunner(conn);runner.setAutoCommit(true);// 读取 SQL 文件Resource resource = resourceLoader.getResource("classpath:" + scriptPath);var reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));// 执行脚本runner.runScript(reader);}}
}
- mybatis 代码自动生成器
package oauth2.config;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;import java.util.Collections;@Component
@DependsOn("sqlScriptExecutor")
public class FastAutoGeneratorConfig {@Value("${spring.datasource.url:''}")private String baseSqlUrl;@Value("${spring.datasource.username:''}")private String baseSqlUser;@Value("${spring.datasource.password:''}")private String baseSqlUserPassword;@PostConstructpublic void init() {// 使用 FastAutoGenerator 快速配置代码生成器FastAutoGenerator.create(baseSqlUrl, baseSqlUser, baseSqlUserPassword).globalConfig(builder -> {builder.author("gch") // 设置作者.disableOpenDir().outputDir("gateway\\src\\main\\java"); // 输出目录}).packageConfig(builder -> {builder.parent("oauth2") // 设置父包名.entity("model") // 设置实体类包名.mapper("dao") // 设置 Mapper 接口包名.service("service") // 设置 Service 接口包名.serviceImpl("service.impl") // 设置 Service 实现类包名.pathInfo(Collections.singletonMap(OutputFile.xml,System.getProperty("user.dir") +"\\gateway\\src\\main\\resources\\mapper"));}).strategyConfig(builder -> {builder.entityBuilder().enableLombok() // 启用 Lombok.enableTableFieldAnnotation() // 启用字段注解.controllerBuilder().enableRestStyle(); // 启用 REST 风格}).templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 模板引擎.execute(); // 执行生成}
}
- SqlVersionMapper 获取未执行sql函数
package oauth2.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import oauth2.model.SqlVersion;import java.util.List;public interface SqlVersionMapper extends BaseMapper<SqlVersion> {List<SqlVersion> selectAllSqlVersionsAndStatusIsFalse();}
<?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="oauth2.dao.SqlVersionMapper"><select id="selectAllSqlVersionsAndStatusIsFalse"resultType="oauth2.model.SqlVersion">select * from sql_version where status = 0</select>
</mapper>
运行效果
- 插入sql版本记录
- sql版本业务数据
- 启动服务程序后查看结果
数据库 sql记录
业务程序mvc框架基础代码