MyBatis-plus+注解形式实现项目与数据库绑定动态更新

前言:在自己电脑上使用mybatis-plus生成映射数据库表的实体类,将项目上传到服务器启动时根据实体类自动创建、更新相应的数据库表,这样做的目的是使得在自己电脑上创建数据库表后不用再到服务器去创建

 实现思路:

  1. 使用mybatis-plus生成实体类
  2. 在生成实体类的时候获取类字段信息并通过注解的形式写入到实体类里面去
  3. 在服务器启动项目的时候获取项目的所有实体类与数据库所有表进行对比,解析实体类上注解信息,写入特定的类用于将数据库没有对应的表进行创建

详细步骤

1.配置mybatis-plus自动生成代码

   依赖mybatis-plus   



    com.baomidou
    mybatis-plus
    2.3


    com.baomidou
    mybatisplus-spring-boot-starter
    1.0.5



    org.freemarker
    freemarker



    org.apache.velocity
    velocity-engine-core
    2.0

 配置

mybatis-plus:
  mapper-locations: classpath*:/mapper/*/*Mapper.xml
  global-config:
    id-type: 3
    field-strategy: 2
    db-column-underline: true
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

 代码生成器

MpGenerator.java

import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.toolkit.StringUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;

public class MpGenerator {

    /**
     * 

* 读取控制台内容 *

*/ public String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } private int type; public PackageConfig getPackageConfig() { PackageConfig pc = new PackageConfig(); if (type == 3) { pc.setParent("com.wdz.tofs"); } else { pc.setParent("com.wdz"); } pc.setModuleName(scanner("模块名")); pc.setEntity("entity"); pc.setController(null); return pc; } /** * 获取表字段信息 */ public ResultSetMetaData getMetaData(DataSourceConfig dsc,String tableName){ Connection connection = dsc.getConn(); try { try (PreparedStatement preparedStatement = connection.prepareStatement("select * from " + tableName)) { return preparedStatement.executeQuery().getMetaData(); } } catch (SQLException e) { e.printStackTrace(); return null; } } /** *

* MySQL 生成演示 *

*/ public static void main(String[] args) { AutoGenerator mpg = new AutoGenerator(); MpGenerator mpGenerator = new MpGenerator(); // 选择 freemarker 引擎,默认 Veloctiy //mpg.setTemplateEngine(new FreemarkerTemplateEngine()); // 全局配置 GlobalConfig gc = new GlobalConfig(); gc.setOpen(false);//完成后不打开文件夹 gc.setAuthor("autor"); String oPath = System.getProperty("user.dir");//得到当前项目的路径 gc.setOutputDir(oPath + "/core_database/src/main/java/"); //生成文件输出根目录 gc.setFileOverride(false);// 是否覆盖同名文件,默认是false gc.setActiveRecord(true);// 不需要ActiveRecord特性的请改为false gc.setEnableCache(false);// XML 二级缓存 gc.setBaseResultMap(true);// XML ResultMap gc.setBaseColumnList(false);// XML columList gc.setIdType(IdType.UUID); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); dsc.setTypeConvert(new MySqlTypeConvert() { // 自定义数据库表字段类型转换【可选】 @Override public DbColumnType processTypeConvert(String fieldType) { // 注意!!processTypeConvert 存在默认类型转换,如果不是你要的效果请自定义返回、非如下直接返回。 if (fieldType.equals("datetime")) { fieldType = "String"; } System.out.println("转换类型:" + fieldType); return super.processTypeConvert(fieldType); } }); dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("00000"); dsc.setUrl("jdbc:mysql://190.289.20.1:3306/***?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT&allowPublicKeyRetrieval=true"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = mpGenerator.getPackageConfig(); mpg.setPackageInfo(pc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); // strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意 strategy.setTablePrefix(new String[]{""});// 此处可以修改为您的表前缀 //下划线转驼峰命名法 strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略 //自动填充设置 List tableFillList = new ArrayList(); tableFillList.add(new TableFill("create_date", FieldFill.INSERT));//设置为插入时自动填充 tableFillList.add(new TableFill("update_date", FieldFill.INSERT_UPDATE));//设置为更新时自动填充 strategy.setTableFillList(tableFillList); strategy.setRestControllerStyle(true); String tables = mpGenerator.scanner("表名,以逗号隔开需要生成的表"); if (tables.length() != 0) { if ((tables.charAt(tables.length() - 1)) == ',') { tables = tables.substring(0, tables.length() - 1); } String[] tabless = tables.split(","); strategy.setInclude(tabless); // 需要生成的表 } strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing Map map = new HashMap<>(); this.setMap(map); } }; // 如果模板引擎是 freemarker // String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity String templatePath = "/templates/mapper.xml.vm"; String entityPath = "/template/entity.java.vm"; // 自定义输出配置 List focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! List list = tableInfo.getFields(); Iterator iterator = list.iterator(); ResultSetMetaData resultSetMetaData = mpGenerator.getMetaData(dsc,tableInfo.getName()); int i = 1; while (iterator.hasNext()){ Map map = new HashMap(); try { //在数据库中字符最大长度 map.put("length",resultSetMetaData.getColumnDisplaySize(i)); //类型 map.put("type",resultSetMetaData.getColumnTypeName(i)); //是否为null map.put("nullable",resultSetMetaData.isNullable(i)); } catch (SQLException e) { e.printStackTrace(); } i++; iterator.next().setCustomMap(map); } return oPath + "/core_database/src/main/resources/mapper/" + mpGenerator.change(tableInfo.getEntityName())+ "/"+tableInfo.getEntityName() + "Mapper.xml"; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); TemplateConfig tc = new TemplateConfig(); //不生成controller tc.setController(null); //不生成xml(xml已经放到resource下面了,这里是不再Java文件夹下面存放了) tc.setXml(null); mpg.setTemplate(tc); // 执行生成 mpg.execute(); } /** * 全部转换为小写 * * @param str 需要转换的参数 * @return 返回替换后字符串 */ private String change(String str) { char[] cs = str.toCharArray(); StringBuilder rstr = new StringBuilder(); for (char c : cs) { //如果输入的是大写,+32即可得到小写 if (c >= 'A' && c <= 'Z') { c += 32; } rstr.append(c); } return rstr.toString(); } }

 自定义表信息以及表字段信息注解

TableInfo(表信息)

import java.lang.annotation.*;

/**
 * 表详细信息
 * @author wuchuancheng
 * @since 2022-07-28
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableInfo {
    /**
     * 表名
     * @return
     */
    String name() default "";

    /**
     * 是否需要自动创建
     * @return
     */
    boolean isCreate() default true;

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

ColumnInfo(表字段信息)

import java.lang.annotation.*;

/**
 * 表字段详细信息
 * @author wuchuancheng
 * @since 2022-07-28
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnInfo {

    /**
     * 字段名
     */
    String name() default "";

    /**
     * 字段注释
     */
    String comment() default "";

    /**
     * 最大长度
     */
    int length() default 0;

    /**
     * 字段类型
     */
    String type() default "";

    /**
     * 是否是主键
     */
    boolean key() default false;

    /**
     * 是否为空
     */
    int nullable() default 0;
}

自定义模板

entity.java.vm(放在resource/template下面)

package ${package.Entity};

//这两个改写成你自己的目录
import com.wdz.annotations.ColumnInfo;
import com.wdz.annotations.TableInfo;

#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${entityLombokModel})

import com.baomidou.mybatisplus.annotations.Version;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
#end

/**
 * 

* $!{table.comment} *

* * @author ${author} * @since ${date} */ @TableInfo(name="${table.name}",comment = "${table.comment}") #if(${entityLombokModel}) @Data #if(${superEntityClass}) @EqualsAndHashCode(callSuper = true) #else @EqualsAndHashCode(callSuper = false) #end @Accessors(chain = true) #end #if(${table.convert}) @TableName("${table.name}") #end #if(${superEntityClass}) public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end { #elseif(${activeRecord}) public class ${entity} extends Model<${entity}> { #else public class ${entity} implements Serializable { #end private static final long serialVersionUID = 1L; ## ---------- BEGIN 字段循环遍历 ---------- #foreach($field in ${table.fields}) #if(${field.keyFlag}) #set($keyPropertyName=${field.propertyName}) #end #if("$!field.comment" != "") /** * ${field.comment} */ #end @ColumnInfo(name="${field.name}",comment = "${field.comment}",length = ${field.customMap.length},type = "${field.customMap.type}",key = ${field.keyFlag},nullable = ${field.customMap.nullable}) #if(${field.keyFlag}) ## 主键 #if(${field.keyIdentityFlag}) @TableId(value = "${field.name}", type = IdType.AUTO) #elseif(!$null.isNull(${idType}) && "$!idType" != "") @TableId(value = "${field.name}", type = IdType.${idType}) #elseif(${field.convert}) @TableId("${field.name}") #end ## 普通字段 #elseif(${field.fill}) ## ----- 存在字段填充设置 ----- #if(${field.convert}) @TableField(value = "${field.name}", fill = FieldFill.${field.fill}) #else @TableField(fill = FieldFill.${field.fill}) #end #elseif(${field.convert}) @TableField("${field.name}") #end ## 乐观锁注解 #if(${versionFieldName}==${field.name}) @Version #end ## 逻辑删除注解 #if(${logicDeleteFieldName}==${field.name}) @TableLogic #end private ${field.propertyType} ${field.propertyName}; #end ## ---------- END 字段循环遍历 ---------- #if(!${entityLombokModel}) #foreach($field in ${table.fields}) #if(${field.propertyType.equals("boolean")}) #set($getprefix="is") #else #set($getprefix="get") #end public ${field.propertyType} ${getprefix}${field.capitalName}() { return ${field.propertyName}; } #if(${entityBuilderModel}) public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { #else public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { #end this.${field.propertyName} = ${field.propertyName}; #if(${entityBuilderModel}) return this; #end } #end #end #if(${entityColumnConstant}) #foreach($field in ${table.fields}) public static final String ${field.name.toUpperCase()} = "${field.name}"; #end #end #if(${activeRecord}) @Override protected Serializable pkVal() { #if(${keyPropertyName}) return this.${keyPropertyName}; #else return null; #end } #end #if(!${entityLombokModel}) @Override public String toString() { return "${entity}{" + #foreach($field in ${table.fields}) #if($!{velocityCount}==1) "${field.propertyName}=" + ${field.propertyName} + #else ", ${field.propertyName}=" + ${field.propertyName} + #end #end "}"; } #end }

第一阶段代码生成器的改造基本完成,这一阶段的作用是根据数据库表生成相对应的实体类,实体类里面还有描述实体类的表信息注解以及字段信息注解,如以下代码类似

import com.wdz.annotations.ColumnInfo;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableName;
import com.wdz.annotations.TableInfo;

import java.io.Serializable;

/**
 * 

* 测试 *

* * @author wuchuancheng * @since 2022-07-28 */ @TableInfo(name="test",comment = "测试") @TableName("test") public class Test extends Model { private static final long serialVersionUID = 1L; /** * 主键 */ @ColumnInfo(name="id",comment = "主键",length = 32,type = "VARCHAR",key = true,nullable = 0) @TableId(value = "id", type = IdType.UUID) private String id; /** * 测试 */ @ColumnInfo(name="test",comment = "测试",length = 255,type = "VARCHAR",key = false,nullable = 1) private String test; public String getWay() { return way; } public void setWay(String way) { this.way = way; } /** * 测试way */ @ColumnInfo(name="way",comment = "测试way",length = 255,type = "VARCHAR",key = false,nullable = 1) private String way; /** * 数字 */ @ColumnInfo(name="ino",comment = "数字",length = 10,type = "INT",key = false,nullable = 1) private Integer int1; /** * 时间 */ @ColumnInfo(name="create_date",comment = "时间",length = 0,type = "DATETIME",key = false,nullable = 1) @TableField(value = "create_date", fill = FieldFill.INSERT) private String createDate; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTest() { return test; } public void setTest(String test) { this.test = test; } public Integer getInt1() { return int1; } public void setInt1(Integer int1) { this.int1 = int1; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } @Override protected Serializable pkVal() { return this.id; } @Override public String toString() { return "Test{" + ", id=" + id + ", test=" + test + ", int1=" + int1 + ", createDate=" + createDate + "}"; } }

另外,mapper.xml会在resource下生成,不会生成controller。

关键代码解释:

这一部分的精髓在这里

// 如果模板引擎是 freemarker
        // String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        String templatePath = "/templates/mapper.xml.vm";
        String entityPath = "/template/entity.java.vm";
        // 自定义输出配置
        List focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                List list = tableInfo.getFields();
                Iterator iterator = list.iterator();
                ResultSetMetaData resultSetMetaData = mpGenerator.getMetaData(dsc,tableInfo.getName());
                int i = 1;
                while (iterator.hasNext()){
                    Map map = new HashMap();
                    try {
                        //在数据库中字符最大长度
                        map.put("length",resultSetMetaData.getColumnDisplaySize(i));
                        //类型
                        map.put("type",resultSetMetaData.getColumnTypeName(i));
                        //是否为null
                        map.put("nullable",resultSetMetaData.isNullable(i));
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    i++;
                    iterator.next().setCustomMap(map);
                }
                return oPath + "/core_database/src/main/resources/mapper/" + mpGenerator.change(tableInfo.getEntityName())+ "/"+tableInfo.getEntityName() + "Mapper.xml";
            }
        });

/templates/mapper.xml.vm模板是在依赖包mybatis-plus-generate下面

/template/entity.java.vm模板是在自己项目resource下面

 resultSetMetaData对象是表字段信息集合

/**
     * 获取表字段信息
     */
    public ResultSetMetaData getMetaData(DataSourceConfig dsc,String tableName){
        Connection connection = dsc.getConn();
        try {
            try (PreparedStatement preparedStatement = connection.prepareStatement("select * from " + tableName)) {
                return preparedStatement.executeQuery().getMetaData();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

而这一段

while (iterator.hasNext()){
                    Map map = new HashMap();
                    try {
                        //在数据库中字符最大长度
                        map.put("length",resultSetMetaData.getColumnDisplaySize(i));
                        //类型
                        map.put("type",resultSetMetaData.getColumnTypeName(i));
                        //是否为null
                        map.put("nullable",resultSetMetaData.isNullable(i));
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    i++;
                    iterator.next().setCustomMap(map);
                }

是将表字段信息写入到实体类里面去,重点在于.setCustomMap();这个方法

源码

将几个数据写入 customMap里面去就可以在entity.java.vm里面使用它

 然后就可以生成带有表信息以及表字段信息注解的实体类了

2.项目启动时创建表

这一步的关键是要去数据库读取所有表名和项目的实体类名然后进行判断,判断出若项目有某个实体类而数据库没有这张表,就自动创建这张表,所以我自建了Table,FieldInfo和与之对应的TableMapper,FieldInfoMapper类来查询数据库表以及表字段、创建或更新表。创建或更新表也可以直接在mapper.xml里面写sql语句

 封装表信息实体类

Table.java

import com.baomidou.mybatisplus.activerecord.Model;

import java.io.Serializable;
import java.util.List;

/**
 * 数据库表名集合
 * @author wuchuancheng
 */
public class Table extends Model {
    private static final long serialVersionUID = 1L;

    private String tableName;

    private String comment;

    private List fieldInfos;

    public List getFieldInfos() {
        return fieldInfos;
    }

    public void setFieldInfos(List fieldInfos) {
        this.fieldInfos = fieldInfos;
    }

    public String getTableName() {
        return tableName;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    @Override
    protected Serializable pkVal() {
        return serialVersionUID;
    }
}

Mapper

TableMapper.java

import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author wuchuancheng
 */
@Mapper
public interface TableMapper extends BaseMapper
{ /** * 查询所以表名 * @return 表集合 */ @Select("select table_name from information_schema.tables where table_schema='bd_dongzhebk' and table_type='base table';") List
tableList(); /** * 创建表 * @param tableInfo 表信息 */ void createTable(Table tableInfo); /** * 更新表 * @param tableInfo 表信息 */ void alterField(Table tableInfo); }

 TableMapper.xml





    
    
        
    
    
    
        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;
        DROP TABLE IF EXISTS ${tableName};
        CREATE TABLE ${tableName}
        
            ${item.name} ${item.type}(${item.length})
            
                CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
            
            NOT NULL
            NULL DEFAULT
                
                    0
                
                
                    NULL
                
            
            COMMENT '${item.comment}',
            
                
                    
                        PRIMARY KEY (${item2.name}) USING BTREE
                    
                
            
        

        ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '${comment}' ROW_FORMAT
        = Compact;
    
    
    
        
            alter table ${tableName} add ${item.name} ${item.type}(${item.length})
            
                CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
            
            NOT NULL
            NULL DEFAULT
                
                    0
                
                
                    NULL
                
            
            comment '${item.comment}';
        
    

封装字段信息实体类

 FieldInfo.java

import com.baomidou.mybatisplus.activerecord.Model;

import java.io.Serializable;

/**
 * 字段信息
 * @author wuchuancheng
 */
public class FieldInfo extends Model {

    private static final long serialVersionUID = 1L;

    private String name;

    private String type;

    private Integer length;

    private Integer decimal;

    private String defaulc;

    public String getDefaulc() {
        return defaulc;
    }

    public void setDefaulc(String defaulc) {
        this.defaulc = defaulc;
    }

    public String getCollation() {
        return collation;
    }

    public void setCollation(String collation) {
        this.collation = collation;
    }

    private Integer isNull;

    private String nullable;

    private String collation;

    private String keyValue;

    public String getKeyValue() {
        return keyValue;
    }

    public void setKeyValue(String keyValue) {
        this.keyValue = keyValue;
    }

    public String getNullable() {
        return nullable;
    }

    public void setNullable(String nullable) {
        this.nullable = nullable;
    }

    private boolean isKey;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Integer getLength() {
        return length;
    }

    public void setLength(Integer length) {
        this.length = length;
    }

    public Integer getDecimal() {
        return decimal;
    }

    public void setDecimal(Integer decimal) {
        this.decimal = decimal;
    }

    public Integer getIsNull() {
        return isNull;
    }

    public void setIsNull(Integer isNull) {
        this.isNull = isNull;
    }

    public boolean isKey() {
        return isKey;
    }

    public void setKey(boolean key) {
        isKey = key;
    }

    public String getAnnotate() {
        return annotate;
    }

    public void setAnnotate(String annotate) {
        this.annotate = annotate;
    }

    private String annotate;

    private String comment;

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    @Override
    protected Serializable pkVal() {
        return serialVersionUID;
    }

    @Override
    public String toString() {
        return "FieldInfo{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", length=" + length +
                ", decimal=" + decimal +
                ", isNull=" + isNull +
                ", isKey=" + isKey +
                ", annotate='" + annotate + '\'' +
                ", comment='" + comment + '\'' +
                '}';
    }
}

 FieldInfoMapper.java

import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @author wuchuancheng
 */
public interface FieldInfoMapper extends BaseMapper {
    
    /**
     * 根据表名查询所有字段信息集合
     * @param tableName 表名
     * @return 字段信息集合
     */
    List getFieldInfos(@Param("tableName") String tableName);
}

FieldInfoMapper.xml





    
    
        
        
        
        
        
        
        
        
        
    
    

功能类

AutomaticConstruct.java

import com.wdz.annotations.ColumnInfo;
import com.wdz.annotations.TableInfo;
import com.wdz.table.entity.FieldInfo;
import com.wdz.table.entity.Table;
import com.wdz.table.mapper.FieldInfoMapper;
import com.wdz.table.mapper.TableMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

@Component
@Order(1)//项目启动时自动执行
public class AutomaticConstruct implements ApplicationRunner {


    /**
     * 获取当前所在环境 根据项目自己更改,作用是判断当前项目是不是在正式服启动
     */
    @Value("${spring.profiles.active}")
    private String tenant;

    @Resource
    private TableMapper tableMapper;

    @Resource
    private FieldInfoMapper fieldInfoMapper;


    /**
     * 将测试服数据库同步到正式服上
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        //如果是正式服环境就做这些事情,这些自动建表的事情
        String tenant = "pro";
        if(tenant.equals(this.tenant)){
            List
iterator = tableMapper.tableList(); List
tables = getTableNames(); boolean bln = true; List
n = new ArrayList<>(); for (Table table : tables) { bln = true; for(Table table1 : iterator){ if((table.getTableName()).equals(table1.getTableName())){ bln = false; List fieldInfos = fieldInfoMapper.getFieldInfos(table.getTableName()); List fieldInfos2 = table.getFieldInfos(); List list = new ArrayList<>(); boolean bln2 = true,bln3 = false; for(FieldInfo fieldInfo:fieldInfos2){ bln2 = true; for(FieldInfo fieldInfo2:fieldInfos){ if((fieldInfo.getName()).equals(fieldInfo2.getName())){ bln2 = false; break; } } if(bln2){ list.add(fieldInfo); bln3 = true; } } //执行更新 if(bln3) { Table table2 = new Table(); table2.setTableName(table.getTableName()); table2.setFieldInfos(list); tableMapper.alterField(table2); } break; } } if(bln){ n.add(table); } } for(Table table:n){ tableMapper.createTable(table); } } } /** * 获取模块中的数据库对应实体类 * @return */ public List
getTableNames(){ List
list = new ArrayList<>(); //spring工具类,可以获取指定路径下的全部类 ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath("com.wdz") + "/**/entity/*.class"; org.springframework.core.io.Resource[] resources =resourcePatternResolver.getResources(pattern); //MetadataReader 的工厂类 MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver); for (org.springframework.core.io.Resource resource : resources) { //用于读取类信息 MetadataReader reader = readerfactory.getMetadataReader(resource); //扫描到的class String classname = reader.getClassMetadata().getClassName(); Class clazz = Class.forName(classname); //判断是否有指定主解 TableInfo anno = clazz.getAnnotation(TableInfo.class); if (anno != null && anno.isCreate()) { //将含有@TableInfo的类的类名放入list Table table = new Table(); table.setTableName(anno.name()); table.setComment(anno.comment()); List fieldInfos = new ArrayList<>(); Field[] fields = clazz.getDeclaredFields(); for(Field field:fields){ field.setAccessible(true); ColumnInfo columnInfo = field.getAnnotation(ColumnInfo.class); if(columnInfo != null){ FieldInfo fieldInfo = new FieldInfo(); fieldInfo.setType(columnInfo.type()); fieldInfo.setName(columnInfo.name()); fieldInfo.setLength(columnInfo.length()); fieldInfo.setKey(columnInfo.key()); fieldInfo.setIsNull(columnInfo.nullable()); fieldInfo.setComment(columnInfo.comment()); fieldInfos.add(fieldInfo); } } table.setFieldInfos(fieldInfos); list.add(table); } } } catch (IOException | ClassNotFoundException ignored) { ignored.printStackTrace(); } return list; } }

AutomaticConstruct类的作用是,在项目启动的时候

  1. 获取数据库所有表集合(调用tableMapper.tableList())
  2. 获取项目所有实体类集合(调用getTableNames())
  3. 两者进行判断,数据库没有的就创建(调用tableMapper.createTable(table))
  4. 数据库有但是表字段不全(调用tableMapper.alterField(table2))更新

3.完成

就这样大功告成了,我把所有需要用到的都贴出来了,复制就可以直接使用,不会有问题。制作不易,还望点个赞。如果有问题可以留言评论。

欢迎大家访问我的博客way博客

你可能感兴趣的:(mybatis,mybatis,java,后端,mysql)