Maven插件开发:根据库表生成实体类&根据实体类生成库表

在码农的日常生活中,复杂度低但是工作量大的工作莫过于根据已有的表写对应的实体类。当字段多的时候,那叫一个苦啊!于是,很多代码生成器应运而生,最流行的莫过于Mybatis-Generator,自动生成Entity、Dao、Mapper,确实是一个不错的工具。但是,以博主的了解,这个工具还是有一些缺点的,算是鸡蛋里挑骨头吧:样式是固定的,有时候不符合我们的习惯(当然,大神可以去改源码)。

现在呢,我们也来开发一个自动生成实体类的工具,准确的说是Maven插件。它可以根据自己配置的数据库连接、用户密码、库表在指定的项目包下生成Java文件,可按需要设置自己的模板。还有一个功能是根据实体类生成库表,这个使用的条件比较苛刻,后面也一并介绍吧。啥也不说,先上实际效果图镇楼!

 

让我们进入学习吧! 

一、了解自定义Maven插件开发。

这里我不会做太多介绍,毕竟我也不是很精通。网上有很多文章写的比较好,比较细,感兴趣的话可以去学习。但是,我也会一步步引导大家,开发出一个完整的Maven插件。

推荐大家使用IDEA进行开发,因为这个工具自带需要的插件,如果使用了Eclipse,要自己网上找教程安装一下。

1、新建项目,选择JDK1.8+,下图所示的archetype,然后点击Next。

 

2、 写项目的坐标,GroupId一般是域名反写,主要说一下ArtifactId:官方给了两种可以使用简写的规范,一种是maven-xxx-plugin,这种命名一般是apache自己的插件用了,我们不使用,免得侵权什么的;第二种就是推荐使用的xxx-maven-plugin这种命名。按规范写的话使用的时候可以简写,就是写"xxx:goal"就好了,比如maven-install-plugin,我们使用的时候直接是install。

3、选择Maven版本。

4、选择目录和项目名称。最后点击Finish即可完成项目构建。

二、pom.xml文件介绍。

 

 

三、定义Table类,用来保存从数据库读到的表的信息。

/**
 * 表的信息
 * @author z_hh
 * @date 2018-04-12
 */
public class Table {
 
    // 表名
    private String tableName;
 
    // 备注
    private String tableRemark;
 
    // 列
    private List<Column> columns = new ArrayList<>();
    
    public String getTableName() {
        return tableName;
    }
 
    public void setTableName(String tableName) {
        this.tableName = tableName;
    }
 
    public String getTableRemark() {
        return tableRemark;
    }
 
    public void setTableRemark(String tableRemark) {
        this.tableRemark = tableRemark;
    }
 
    public List<Column> getColumns() {
        return columns;
    }
 
    public void setColumns(List<Column> columns) {
        this.columns = columns;
    }
 
    class Column {
        // 名称
        private String columnName;
        // 类型
        private String columnType;
        // 长度
        private Integer columnSize;
        // 是否可为空
        private Integer columnNullable;
        // 默认值
        private String columnDefaultValue;
        // 备注
        private String columnRemark;
 
        public String getColumnName() {
            return columnName;
        }
 
        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }
 
        public String getColumnType() {
            return columnType;
        }
 
        public void setColumnType(String columnType) {
            this.columnType = columnType;
        }
 
        public Integer getColumnSize() {
            return columnSize;
        }
 
        public void setColumnSize(Integer columnSize) {
            this.columnSize = columnSize;
        }
 
        public Integer getColumnNullable() {
            return columnNullable;
        }
 
        public void setColumnNullable(Integer columnNullable) {
            this.columnNullable = columnNullable;
        }
 
        public String getColumnDefaultValue() {
            return columnDefaultValue;
        }
 
        public void setColumnDefaultValue(String columnDefaultValue) {
            this.columnDefaultValue = columnDefaultValue;
        }
 
        public String getColumnRemark() {
            return columnRemark;
        }
 
        public void setColumnRemark(String columnRemark) {
            this.columnRemark = columnRemark;
        }
 
        @Override
        public String toString() {
            return "Column{" +
                    "columnName='" + columnName + '\'' +
                    ", columnType='" + columnType + '\'' +
                    ", columnSize=" + columnSize +
                    ", columnNullable=" + columnNullable +
                    ", columnDefaultValue='" + columnDefaultValue + '\'' +
                    ", columnRemark='" + columnRemark + '\'' +
                    '}';
        }
 
    }
 
    @Override
    public String toString() {
        return "Table{" +
                "tableName='" + tableName + '\'' +
                ", tableRemark='" + tableRemark + '\'' +
                ", columns=" + columns +
                '}';
    }
}
四、定义需要生成的文件信息类。

/**
 * Java文件信息
 *
 * @author z_hh
 * @version 1.0
 * @since 2018/4/12
 */
public class JavaFileInfo {
 
    /**
     * 文件名
     */
    private String fileName;
 
    /**
     * 文本内容
     */
    private String text;
 
    private JavaFileInfo() {}
 
    public static JavaFileInfo create() {
        return new JavaFileInfo();
    }
 
    public String getFileName() {
        return fileName;
    }
 
    public JavaFileInfo setFileName(String fileName) {
        this.fileName = fileName;
        return this;
    }
 
    public String getText() {
        return text;
    }
 
    public JavaFileInfo setText(String text) {
        this.text = text;
        return this;
    }
 
    @Override
    public String toString() {
        return "JavaFileInfo{" +
                "fileName='" + fileName + '\'' +
                ", text='" + text + '\'' +
                '}';
    }
}
五、定义最重要的类,插件执行的主类。

类的goal注解和父类

成员变量,根据注解从用户配置那里取到属性值 

/**
     * path of the source folder.
     * @parameter expression="${sourceFolderPath}"
     * @required
     */
    private String sourceFolderPath;
 
    /**
     * name of the package.
     * @parameter expression="${packageName}"
     * @required
     */
    private String packageName;
 
    /**
     * url of the database.
     * @parameter expression="${dbUrl}"
     * @required
     */
    private String dbUrl;
 
    /**
     * user of the the database.
     * @parameter expression="${dbUser}"
     * @required
     */
    private String dbUser;
 
    /**
     * password of the the database.
     * @parameter expression="${dbPwd}"
     * //@required
     */
    private String dbPwd;
 
    /**
     * table name of generator java file.
     * @parameter expression="${tableNames}"
     */
    private String[] tableNames;
 
    /**
     * type of the database, for example:oracle、mysql.
     */
    private String dbType;
重写execute方法,插件处理逻辑写在这里面

参数校验以及识别数据库类型

获取表信息并封装到自定义Table类

private List<Table> geTables(String[] tableNames) throws Exception {
        Connection connection = getConnection();
        DatabaseMetaData dbmd = connection.getMetaData();
 
        ResultSet resultSet = dbmd.getTables(null, "%", "%", new String[] { "TABLE" });
        List<Table> tables = new ArrayList<>();
        while (resultSet.next()) {
            String tableName = resultSet.getString("TABLE_NAME");
            boolean need = Arrays.stream(tableNames).anyMatch(name -> name.equalsIgnoreCase(tableName));
            boolean contains = tables.parallelStream().anyMatch(table -> table.getTableName().equalsIgnoreCase(tableName));
            if (!need || contains) {
                continue;
            }
            getLog().info("正在分析表" + tableName + "...");
 
            Table table = new Table();
            table.setTableName(tableName);
            table.setTableRemark(resultSet.getString("REMARKS"));
 
            List<Table.Column> columns = table.getColumns();
            ResultSet rs = null;
            if (Utils.ORACLE.equalsIgnoreCase(dbType)) {
                rs = dbmd.getColumns(null, getSchema(connection), tableName.toUpperCase(), "%");
            }
            // 除了oracle和db2其它这么用
            else {
                rs = dbmd.getColumns(null, "%", tableName, "%");
            }
 
            while (rs.next()) {
                Table.Column column = table.new Column();
                column.setColumnName(rs.getString("COLUMN_NAME"));
                column.setColumnType(rs.getString("TYPE_NAME"));
                column.setColumnSize(rs.getInt("COLUMN_SIZE"));
                column.setColumnNullable(rs.getInt("NULLABLE"));
                column.setColumnDefaultValue(rs.getString("COLUMN_DEF"));
                column.setColumnRemark(rs.getString("REMARKS"));
                columns.add(column);
            }
 
            getLog().info("读取到" + columns.size() + "个字段");
            tables.add(table);
        }
 
        connection.close();
        return tables;
    }
创建Java文件

 根据Table得到JavaFIleInfo对象

/**
     * 生成DO对象文本
     * @param table
     * @return
     */
    private JavaFileInfo createDoJavaFileInfo(Table table) {
        String tableName = table.getTableName(),
                className = tableName.substring(0, 1).toUpperCase()
                        + lineToHump(tableName).substring(1);
        getLog().info("正在生成类" + className + "...");
 
        // 替换类名、包名、表名
        String classText = classTemplateText.replace("${packageName}", packageName)
            .replace("${className}", className).replace("${tableName}", tableName);
 
        List<String> fieldTexts = new ArrayList<>();
        List<String> getSetMethodTexts = new ArrayList<>();
        table.getColumns().stream().forEach(column -> {
            String name = lineToHump(column.getColumnName()),
                    type = typeMap.get(column.getColumnType()),
                    nullAble = Objects.equals(column.getColumnNullable(), 1) ? "可空" : "非空",
                    remark = Optional.ofNullable(column.getColumnRemark()).orElse(""),
                    getterSetterName = name.substring(0, 1).toUpperCase() + name.substring(1);
            // 得到一个字段的声明、get方法、set方法
            String fieldText = fieldTemplateText.replace("${fieldRemark}", remark)
                .replace("${otherInfo}", "长度:" + column.getColumnSize() + ","
                        + nullAble + ",默认值:" + column.getColumnDefaultValue())
                .replace("${fieldType}", type).replace("${fieldName}", name);
            String getSetMethodText = getSetMethodTemplateText.replace("${fieldType}", type)
                .replace("${fieldName}", name).replace("${u_fieldName}", getterSetterName);
 
            fieldTexts.add(fieldText);
            getSetMethodTexts.add(getSetMethodText);
        });
 
        // 得到全部字段的声明语句
        String fieldPart = fieldTexts.stream().reduce((f1, f2) -> f1 + f2).orElse("// 什么也没有^_-_^");
        // 得到全部get方法、set方法的语句
        String getSetMethodPart = getSetMethodTexts.stream().reduce((f1, f2) -> f1 + "\n" + f2).orElse("// 什么也没有^_-_^");
 
        // 替换类模板文件的字段部分和方法部分
        String classFinalText = classText.replace("@{fieldPart}", fieldPart)
            .replace("${getSetMethodPart}", getSetMethodPart);
 
        return JavaFileInfo.create().setFileName(className).setText(classFinalText.toString());
    }
在指定包生成对应实体类文件

最后运行效果

 

 剩下内容和根据实体类生成库表将在后面讲解,敬请期待!!!
存在问题及修改意向:生成的实体类模板,字段声明模板,注释模板,getset方法模板,现在都是写死在代码里面的,要修改的话必须要动源代码,这样很不方便。所以后续打算,将这些模板内容写在外部文件里,然后在插件配置里面写上文件路径,为空的话就使用默认的模板。

项目完整代码已上传,前往下载
————————————————
版权声明:本文为CSDN博主「ThunderclapT丶」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_31142553/article/details/81256516

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值