通過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

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