# LowCode 低代码建表工具

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 建表结束...");
    }
}

项目地址

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值