# LowCode 低代码建表工具

news2024/12/22 20:42:49

LowCode 低代码建表工具

需求描述

  • 将数据库的表映射为实体类,服务启动时,扫描表相关的实体类,根据实体类模型在数据库创建相关的表

依赖

  • 主要依赖:使用 Sprintboot、druid、spring-jdbc、mybatis
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.11</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.8.RELEASE</version>
    <scope>compile</scope>
</dependency>

<!-- Mybatis 的依赖  -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis.version}</version>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>

设计思路

  • 使用Springboot CommandLineRunner机制
  • 开发自定义数据库模型表注解,扫描所有的表注解
  • 查询数据库库、表、字段等元数据信息
  • 比对元数据和扫描到的表模型信息,确定新增和更新的表模型
  • 执行新增和更新操作

代码工程

  • annotation:自定义数据库模型表、字段、索引等注解
  • init:主要扫描表信息,比对元数据建表的代码
  • constants:常量类
  • utils:工具类
    在这里插入图片描述

业务流程图

  • lowcode-database业务流程图大致如下
    在这里插入图片描述

类图

lowcode-database建表接口

  • 调用此接口中的方法进行建表的操作
    在这里插入图片描述

lowcode-database主要类图

  • 建表管理器,主要负责获取不同数据库的元数据信息,针对不同的数据库进行建表、更新表的操作

  • 总类图
    在这里插入图片描述

  • 放大部分图
    在这里插入图片描述

工厂类

在这里插入图片描述

自定义表注解定义

  • Table.java
package com.lidong.lowcode.database.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.lidong.lowcode.database.constants.database.CharacterSetEnum;
import com.lidong.lowcode.database.constants.database.EngineEnum;
import com.lidong.lowcode.database.constants.enumconst.StrCaseEnum;
import com.lidong.lowcode.database.constants.database.TableOrderEnum;

/**
 * 实体类表注解
 *
 * @author LiDong
 * @version 1.0.0
 * @createTime 9/7/2022 8:39 AM
 */
// This annotation is used to Class
@Target(ElementType.TYPE)
// When  run is  effective
@Retention(RetentionPolicy.RUNTIME)
// Java document annotation
@Documented
public @interface Table {

    /**
     * 表名
     *
     * @return String
     */
    String name() default "";

    /**
     * 表名大小写
     *
     * @return StrCaseEnum
     */
    StrCaseEnum tableNameCase() default StrCaseEnum.LOWER;

    /**
     * 引擎
     *
     * @return EngineEnum
     */
    EngineEnum engine() default EngineEnum.INNODB;

    /**
     * 自增开始数值
     *
     * @return int
     */
    int autoIncrementNum() default 0;

    /**
     * 字符集
     *
     * @return CharacterSetEnum
     */
    CharacterSetEnum characterSet() default CharacterSetEnum.UTF_8;

    /**
     * 表排序类型
     *
     * @return TableOrderEnum
     */
    TableOrderEnum orderType() default TableOrderEnum.UTF8_GENERAL_CI;

    /**
     * 表备注
     *
     * @return String
     */
    String comment() default "";
}

扫描自定义注解标志的类

  • Spring ResourceLoader为我们通过资源路径getResource()获取外部资源提供了统一的方法
  • 要获取ResourceLoader的引用,需要实现ResourceLoaderAware接口。
public class LowCodeDataBaseResourceLoader implements ResourceLoaderAware {

    private static final Logger logger = LoggerFactory.getLogger(LowCodeDataBaseResourceLoader.class);
    public static final String PATTERN_CLASS = "/**/*.class";

    /**
     * 需要扫描的
     */
    private final List<TypeFilter> includeTypeFilterList = new LinkedList<>();
    /**
     * 不需要扫描的
     */
    private final List<TypeFilter> excludeTypeFilterList = new LinkedList<>();

    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    /**
     * 获取包下面注解标注的类
     *
     * @param basePackages 包路径 com.lidong.lowcode
     * @param annotations  需要扫描的注解
     * @return List
     */
    public static <T> List<Class<T>> getAllClassByAnnotation(String[] basePackages, Class<? extends Annotation>... annotations) {
        LowCodeDataBaseResourceLoader lowCodeDataBaseResourceLoader = new LowCodeDataBaseResourceLoader();
        if (!ObjectUtils.isEmpty(annotations)) {
            for (Class<? extends Annotation> anooationClass : annotations) {
                lowCodeDataBaseResourceLoader.addIncludeFilter(new AnnotationTypeFilter(anooationClass));
            }
        }
        List<Class<T>> tableClassList = new ArrayList<>();
        for (String pack : basePackages) {
            tableClassList.addAll(lowCodeDataBaseResourceLoader.scanPackageGetClassList(pack));
        }
        return tableClassList;
    }

    /**
     * 扫描包获取类
     *
     * @param basePackage 包路径
     * @param <T>         T
     * @return List
     */
    public <T> List<Class<T>> scanPackageGetClassList(String basePackage) {
        List<Class<T>> classes = new ArrayList<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + org.springframework.util.ClassUtils
                    .convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + PATTERN_CLASS;
            Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    if (matchTypeFilter(metadataReader)) {
                        try {
                            ClassMetadata classMetadata = metadataReader.getClassMetadata();
                            Class<?> aClass = Class.forName(classMetadata.getClassName());
                            classes.add((Class<T>) aClass);
                        } catch (Exception e) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new InitDataBaseException(e.getMessage(), e);
        }
        return classes;
    }

    /**
     * 添加需要扫描的规则
     *
     * @param includeFilter includeFilter
     */
    private void addIncludeFilter(TypeFilter includeFilter) {
        includeTypeFilterList.add(includeFilter);
    }

    /**
     * 匹配过滤规则
     *
     * @param metadataReader metadataReader
     * @return boolean
     * @throws IOException exception
     */
    private boolean matchTypeFilter(MetadataReader metadataReader) throws IOException {
        for (TypeFilter typeFilter : excludeTypeFilterList) {
            if (typeFilter.match(metadataReader, metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter typeFilter : includeTypeFilterList) {
            if (typeFilter.match(metadataReader, metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }
}

InitDataBaseStarter建表启动器

/**
 * 数据库自动配置启动类
 *
 * @author LiDong
 * @version 1.0.0
 * @createTime 9/9/2022 8:15 PM
 */
@Component
@Order(Integer.MIN_VALUE)
public class InitDataBaseStarter implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(InitDataBaseStarter.class);

    /**
     * 数据库自动配置建表入口
     *
     * @param args args
     * @throws Exception exception
     */
    @Override
    public final void run(String... args) throws Exception {
        logger.info("lowcode-database 建表开始...");
        // 创建数据库建表记录表
        InitDataBase initDataBase = LowcodeDbSpringContextUtils.getBean(InitDataBase.class);
        // 创建数据库建表固化表
        initDataBase.createCuringTable();
        List<DataBaseTable> dataBaseTableList = initDataBase.scanAnnotationGetTableInfo();
        logger.info("lowcode-database 共扫描到表 {} 个", dataBaseTableList.size());
        initDataBase.start(dataBaseTableList);
        logger.info("lowcode-database 建表结束...");
    }
}

项目地址

  • https://gitee.com/Marlon_Brando/lowcode_back/tree/develop/lowcode/lowcode-database

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/113956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为啥这些开源的网络框架这么强

hi&#xff0c; 大家好&#xff0c;我是大师兄&#xff0c;今天分享一下网络编程下半部分内容&#xff0c;主要分享开源网络io框架用到了哪些核心技术&#xff0c;使他们如此流行&#xff0c;这些技术值得我们学习&#xff0c;可以增加我们编程技巧和优化思路。只有掌握更多技能…

【类和对象(上)】

Quitters never win and winners never quit. 目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 4.1 访问限定符 4.2 封装 5.类的作用域 6.类的实例化 7.类对象模型 7.1 如何计算类对象的大小 7.2 结构体内存对齐规则 8.this指针 …

理解 Proxy 和 Reflect

03_02_理解 Proxy 和 Reflect 一、开始之前: 为什么还会有这一篇文章呢&#xff1f;不是手写mini-vue吗&#xff1f;其实可以理解成支线任务、番外篇&#xff0c;是对主线内容的补充。 这一篇文章可能文字比较多&#xff0c;理论知识比较多&#xff0c;参考了4本书相关的章节…

九、Express 基本使用(简)

前一篇内容讲到Express框架的安装以及对Express项目的目录文件有一定的认识了解之后&#xff0c;使用Express创建了最基本的一个Web服务器&#xff0c;接下来进行对Express框架的一些内容来做一个基本的使用&#xff1b; 创建 Web 服务器 node 或 nodemon 执行app.js文件&#…

踩坑了、踩到一个特别无语的常识坑

大家好 踩坑了啊&#xff0c;又踩坑了啊&#xff01; 这次踩到一个特别无语的常识坑。知道真相的那一刻&#xff0c;人就是整个麻掉。 先上个代码&#xff1a; private static double calculate(double a, int b) {return a / b; } 复制代码 你先别问为什么计算不用 BigDec…

RxJS初认识

概念&#xff1a; RxJS的运行就是Observable和Observer之间的互动游戏。 Observable就是“可以被观察的对象”&#xff0c;即“可被观察者”&#xff0c;而Observer就是‘观察者’&#xff0c;连接两者的桥梁就是Observable对象的函数subscribe。 RxJS中的数据流就是Observable…

第二十三章 数论——质数(1)(超级详细的推导)

第二十三章 数论——质数一、什么是质数二、质数的判断1、试除法&#xff08;朴素版&#xff09;2、试除法&#xff08;优化版&#xff09;三、分解质因数1、什么是质因数2、算术基本定理3、分解质因数&#xff08;1&#xff09;问题&#xff08;2&#xff09;思路&#xff08;…

RepNAS: 基于NAS的结构重参数化技术

1. 介绍 在过去几年里&#xff0c;NAS技术取得了长足进展。然而&#xff0c;由于搜索约束与实际推理之间的差异导致高效网络搜索仍极具挑战性。为搜索一个具有高性能、低推理延迟的模型&#xff0c;已有方案往往在算法中添加计算复杂度约束。然而&#xff0c;推理速度会受多种…

【强化学习笔记】马尔可夫过程、马尔可夫奖励过程

文章目录1.马尔可夫过程1.1.随机过程1.2.马尔可夫性质1.3.马尔可夫过程2. 马尔可夫奖励过程2.1.回报2.2.价值函数3.马尔可夫决策过程1.马尔可夫过程 马尔可夫过程&#xff08;Markov process&#xff09; 指具有 马尔可夫性质 的 随机过程 &#xff0c;也被称为马尔可夫链&…

C++GUI之wxWidgets(4)-编写应用涉及的类和方法(2)-wxDialog,wxCloseEvent

目录wxDialog包含类继承具体描述模态和无模态支持样式此类发出的事件wxWindow:&#xff1a;Close()wxCloseEvent具体描述使用此类的事件wxDialog 包含 #include <wx/dialog.h>类继承 描述主 具体描述 对话框是一个带有标题栏的窗口&#xff0c;有时还有一个系统菜单…

python-多线程、网络编程、正则表达式

目录 闭包 多线程 主线程 线程阻塞 同步锁 网络编程 正则表达式 re.match函数 re.search方法 re.match与re.search的区别 re.findall()方法 正则表达式的特殊规则 闭包 account0 def atm(num,flag):global accountif flag:accountnumaccountprint(account)else:acco…

免费开源的高精度OCR文本提取,支持 100 多种语言、自动文本定位和脚本检测,几行代码即可实现离线使用(附源码)

免费开源的高精度OCR文本提取,支持 100 多种语言、自动文本定位和脚本检测,几行代码即可实现离线使用(附源码)。 要从图像、照片中提取文本吗?是否刚刚拍了讲义的照片并想将其转换为文本?那么您将需要一个可以通过 OCR(光学字符识别)识别文本的应用程序。 图片文字识…

html圣诞树代码

一、前言 想做一个圣诞树&#xff0c;通过html实现了下 二、效果展示 三、代码 <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <meta name"viewport" cont…

245. 你能回答这些问题吗——线段树

给定长度为 N 的数列 A&#xff0c;以及 M 条指令&#xff0c;每条指令可能是以下两种之一&#xff1a; 1 x y&#xff0c;查询区间 [x,y] 中的最大连续子段和&#xff0c; 2 x y&#xff0c;把 A[x] 改成 y。 对于每个查询指令&#xff0c;输出一个整数表示答案。 输入格式…

RabbitMQ 第二天 高级 9 RabbitMQ 集群搭建 9.3 集群管理 9.5 负载均衡-HAProxy

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第二天 高级9 RabbitMQ 集群搭建9.3 集群管理9.5 负载均衡-HAProxy9.5.1 安装HAProxy9.5.2 配置HAProxy第二天 高级 9 RabbitMQ 集群搭建 9.3 集群管理 rabbitmqctl join_cl…

MariaDB上市:MySQL之父奋斗13年终敲钟 要写代码写到100岁

雷递网 雷建平 12月24日云数据库公司MariaDB日前与特殊目的公司Angel Pond Holdings完成合并&#xff0c;并在纽交所上市&#xff0c;新公司更名为MariaDB。MariaDB是2022年初与Angel Pond Holdings达成合并协议&#xff0c;对新公司的作价为6.72亿美元。MariaDB是MySQL之父Mic…

【技术应用】java基于UNIX域套接字(unix domain socket)连接redis

【技术应用】java基于UNIX域套接字unix domain socket连接redis一、前言二、实现思路三、代码实现1、java socket基于redis.sock连接redis2、Lettuce框架基于redis.sock连接redis一、前言 在公司工作中经常涉及到一些中小型项目&#xff0c;这些项目都会涉及使用redis数据库&a…

Redis5.0+——持久化——RDBAOF

Redis持久化-RDB 1.实现目标&#xff1a; 在redis持久化时&#xff0c;持久化dump.rdb文件放入到redis解压目录下的data目录下的6379目录下 2.前期准备 1.在redis-5.0.3解压目录下新建data数据目录 2.编辑前面配置的/etc/redis.conf配置文件 修改持久化文件位置 (1) 进入安…

MySQL热备之PXB备份与恢复

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

【语音处理】使用块反射器的基于DFT的系统中用于旁瓣抑制的正交预编码(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…