通过Velocity模板引擎自动将Mysql表生成JavaBean实体类的Gradle插件

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载

1. 需求

最近遇到一个需求,需要写一段程序,监听Mysql数据库数据变化,对Cassandra的数据进行同步。但是现在Mysql里面是有数据的,Cassandra中没有数据,如果要保持同步,就需要程序跑起来的时候首先将Mysql中的数据批量插入到Cassandra中,然后监听Mysql变化,对Cassandra做增量处理。

在写程序的时候碰到一个问题,需要对Mysql中的表创建对应的实体类JavaBean对象,最开始的时候是通过Mysql建表语句一个个手动写的,无奈表太多,所以需要写一个自动生成JavaBean的Gradle插件。这个插件能自动连接Mysql数据库,查询出所有的表及其字段,然后使用Velocity模板引擎自动组建实体类文件,从而实现JavaBean类的自动生成。

如需深入学习Gradle相关知识,请移步《Gradle深度揭秘》

插件使用

在这里插入图片描述

2. 插件编写

2.1 gradle配置

//buildSrc下build.gradle
apply plugin: 'groovy'
apply plugin: 'java'
sourceCompatibility = 1.8
dependencies {
    compile gradleApi()
    compile localGroovy()
}
repositories {
    mavenCentral()
}
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
tasks.withType(GroovyCompile) {
    options.encoding = "UTF-8"
}
dependencies{
    // https://mvnrepository.com/artifact/org.apache.velocity/velocity
    implementation group: 'org.apache.velocity', name: 'velocity', version: '1.7'
    //https://mvnrepository.com/artifact/mysql/mysql-connector-java
    implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.20'
}

2.2 JDBC

需要通过JDBC从mysql中查询所有表及字段

public class MysqlHelper {
    Connection conn;
    BeanDataSource dataSource;

    public MysqlHelper(BeanDataSource dataSource){
        this.dataSource = dataSource;
        try {
            Class.forName(dataSource.getDriver());
            conn = DriverManager.getConnection(dataSource.getUrl(), dataSource.getUserName(), dataSource.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询mysql中所有的表
     * @return
     */
    List<String> getAllTable(){
        List<String> tables = new ArrayList<>();
        String sql_gettables = String.format("select TABLE_NAME from information_schema.tables where table_schema='%s'",dataSource.getDbName());
        try {
            Statement statement = conn.createStatement();
            ResultSet resultSet =  statement.executeQuery(sql_gettables);
            while(resultSet.next()) {
                tables.add(resultSet.getString("TABLE_NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return tables;
    }

    /**
     * 查询指定表的字段集合
     * @param tableName
     * @return
     */
    List<Column> getTableColumns(String tableName){
        String sql_getCloumns = String.format("select COLUMN_NAME, COLUMN_TYPE, COLUMN_KEY from information_schema.columns" +
                " where table_schema='%s' and table_name='%s'",dataSource.getDbName(), tableName);
        List<Column> columnList = new ArrayList<>();
        try {
            Statement statement = conn.createStatement();
            ResultSet resultSet =  statement.executeQuery(sql_getCloumns);
            while(resultSet.next()) {
                columnList.add(new Column(
                        resultSet.getString("COLUMN_NAME"),
                        resultSet.getString("COLUMN_TYPE")));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return columnList;
    }
}

//mysql字段对象封装
class Column {
    String fieldName  // 实体类属性名
    String fieldType  // 实体类属性类型
    Column(String mysqlColumnName, String columnType) {
        this.fieldName = mysqlColumnName
        this.fieldType = column2FieldType(columnType)
    }
	String column2FieldType(String columnType){...}
}

2.3 Velocity模板引擎

首先在resources目录下定义一个.vm格式的JavaBean类的模板文件

package ${packageName};
import lombok.Data;
/**
 * Author: openXu
 * Time: ${time}
 * class: ${className}
 * Description:
 */
@Data
public class  ${className} {

#foreach($columnProperty in $columns)
    private ${columnProperty.fieldType} ${columnProperty.fieldName};
#end
}

编写工具类,使用Velocity自动生成实体类文件内容,并写入对应.java文件中

class VelocityFactory {

    static{
        //配置velocity的资源加载路径
        Properties velocityPros = new Properties()
        velocityPros.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath")
        velocityPros.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName())
        velocityPros.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8")
        velocityPros.setProperty(Velocity.INPUT_ENCODING, "UTF-8")
        velocityPros.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8")
        Velocity.init(velocityPros)
    }

    /**
     * 根据模板生成JavaBean文件内容
     * @param packageName 包名
     * @param tableName mysql表名
     * @param columnList 表字段集合
     * @return
     */
    static String getVmContent(String packageName, String className, List<Column> columnList){
        //绑定velocity数据
        VelocityContext context = new VelocityContext()
        context.put("packageName", packageName)    //实体类分包 com.openxu.bean
        context.put("time", new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()))   //实体类生成时间2020/3/30 14:31
        context.put("className", className)
        context.put("columns", columnList)
        //根据模板生成java文件内容
        Template template = Velocity.getTemplate("bean.vm")
        StringWriter writer = new StringWriter()
        template.merge(context, writer)
        writer.flush()
        writer.close()
        return writer.toString()
    }
}

2.4 定义Gradle插件

由于有一些内容需要我们使用插件时进行配置,比如mysql url用户名密码等,以及我们JavaBean存放的文件夹,所以创建一个扩展类,方便使用插件时对插件进行配置

class BeanDataSource {
	//需要配置项
    String driver
    String url
    String userName
    String password
    String packageName   //bean输出目录
    //数据库名称(自动截取)
    String dbName
    BeanDataSource build() {
        dbName = url.substring(url.lastIndexOf("/")+1, url.lastIndexOf("?"))
        System.out.println("---------数据库名称:"+dbName+"   bean包名:"+packageName)
        return this
    }
}

定义一个插件类,在插件被应用时创建一个名为beanDataSource的扩展对象,类型为上面定义的扩展类。然后创建一个名为autoBean的Task,运行这个任务就可以自动连接Mysql生成对应JavaBean类文件

/**
 * Author: openXu 
 * Time: 2020/5/15 11:07
 * class: AutoBeanPlugin
 * Description: 自定义Gradle插件,根据数据源自动生成JavaBean
 */
class AutoBeanPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        // 创建一个名为beanDataSource的扩展,在build.gradle中配置它
        def beanDataSource = project.extensions.create('beanDataSource', BeanDataSource.class)
        // 创建一个名为autoBean的任务,用于读取Mysql数据,自动创建bean
        project.task("autoBean", type:AutoBeanTask, group:"help")
        project.tasks.getByName("autoBean").doFirst {
            //将配置信息作为Task的参数
            ext.beanDataSource = beanDataSource.build()
        }
    }
}
class AutoBeanTask extends DefaultTask{

    /**任务执行体 */
    @TaskAction
    def generateBean(){
        String outPath = String.format("%s/src/main/java/%s",project.getProjectDir(), beanDataSource.packageName.replaceAll("\\.", "/"))
        System.out.println("输出路径:"+outPath)
        //从mysql中查询表及字段
        MysqlHelper helper = new MysqlHelper(beanDataSource)
        List<String> tableList = helper.getAllTable()
        for(String tableName : tableList){
            //获取表字段集合
            List<Column> columnList = helper.getTableColumns(tableName)
            //根据模板创建java文件
            writeFile(outPath, tableName, columnList)
        }
    }

    /**
     * 根据Mysql表及字段集合,通过Velocity模板自动生成实体类代码,写入对应类文件中
     * @param filePath 实体类存放路径
     * @param tableName 表名
     * @param columnList 字段集合
     */
     void writeFile(String filePath, String tableName, List<Column> columnList) {
         //获取实体类名
        String className = StringUtil.underline2PascalStyle(tableName)
        //获取文件内容
        String classContent = VelocityFactory.getVmContent(beanDataSource.packageName, className, columnList)
        File dir = new File(filePath)
        if (!dir.exists())
            dir.mkdirs()
        File file = new File(dir, className+".java")
        java.io.FileWriter writer = null
        try {
            writer = new java.io.FileWriter(file)
            writer.write(classContent)
        } catch (IOException e) {
            throw new RuntimeException("Error creating file " + className, e)
        } finally {
            if (writer != null) {
                try {
                    writer.close()
                } catch (IOException e) {
                }
            }
        }
    }
}

2.5 插件发布到Maven

resources.META-INF.gradle-plugins下创建插件配置文件mysqlautobean.plugin.properties

implementation-class=com.openxu.autobean.AutoBeanPlugin

添加发布脚本

//插件发布
apply plugin: 'maven-publish'
publishing {
    publications {
        mavenJava(MavenPublication) {
            groupId = "msyql.auto.javabean"
            artifactId = "plugin"
            version = "1.0.0"
            from components.java
        }
    }
    //配置仓库目录
    repositories {
        maven {
            url uri('../repos')
        }
    }
}

gradle配置完成之后,运行gradlew :buildSrc:publish,插件就被发布到当前工程目录下的repos文件夹中了

3. 插件使用

//root build.gradle
buildscript {
    repositories {
		...
        maven {
            url uri('repos')
        }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'msyql.auto.javabean:plugin:1.0.0'
    }
}

//app build.gradle
apply plugin: 'com.android.application'
apply plugin: 'mysqlautobean.plugin'

android {
    ...
}
// 配置插件
beanDataSource{
    driver = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/openXu?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&useCursorFetch=true&defaultFetchSize=1000"
    userName = "root"
    password = "root"
    packageName = "com.openxu.autobean"   //实体类输出目录
}

dependencies {
	...
	//lombok自动资源管理,可以为JavaBean自动生成getter、setter、equals、hashCode和toString等等
    // https://mvnrepository.com/artifact/org.projectlombok/lombok
    implementation group: 'org.projectlombok', name: 'lombok', version: '1.18.12'
}

4. 源码

如需深入学习Gradle相关知识,请移步《Gradle深度揭秘》

AutoBeanGradlePlugin on GitHub By openXu

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章