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方法模板,现在都是写死在代码里面的,要修改的话必须要动源代码,这样很不方便。所以后续打算,将这些模板内容写在外部文件里,然后在插件配置里面写上文件路径,为空的话就使用默认的模板。

项目完整代码已上传,前往下载

猜你喜欢

转载自blog.csdn.net/qq_31142553/article/details/81256516