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

Posted open-Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过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

以上是关于通过Velocity模板引擎对Mysql表自动生成JavaBean实体类的Gradle插件的主要内容,如果未能解决你的问题,请参考以下文章

使用 Velocity 模板引擎快速生成代码

基于 POI 实现一个 Excel 模板引擎

模板引擎之Velocity脚本基本的语法全

Apache Solr Velocity模板注入RCE漏洞复现

Velocity

Velocity 模板引擎介绍