版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载
最近遇到一个需求,需要写一段程序,监听Mysql数据库数据变化,对Cassandra的数据进行同步。但是现在Mysql里面是有数据的,Cassandra中没有数据,如果要保持同步,就需要程序跑起来的时候首先将Mysql中的数据批量插入到Cassandra中,然后监听Mysql变化,对Cassandra做增量处理。
在写程序的时候碰到一个问题,需要对Mysql中的表创建对应的实体类JavaBean对象,最开始的时候是通过Mysql建表语句一个个手动写的,无奈表太多,所以需要写一个自动生成JavaBean的Gradle插件。这个插件能自动连接Mysql数据库,查询出所有的表及其字段,然后使用Velocity
模板引擎自动组建实体类文件,从而实现JavaBean类的自动生成。
深入学习Gradle相关知识,请移步《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'
}
需要通过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 getAllTable(){
List 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 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 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){...}
}
首先在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 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()
}
}
由于有一些内容需要我们使用插件时进行配置,比如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 {
@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 tableList = helper.getAllTable()
for(String tableName : tableList){
//获取表字段集合
List columnList = helper.getTableColumns(tableName)
//根据模板创建java文件
writeFile(outPath, tableName, columnList)
}
}
/**
* 根据Mysql表及字段集合,通过Velocity模板自动生成实体类代码,写入对应类文件中
* @param filePath 实体类存放路径
* @param tableName 表名
* @param columnList 字段集合
*/
void writeFile(String filePath, String tableName, List 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) {
}
}
}
}
}
在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
文件夹中了
//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'
}
深入学习Gradle相关知识,请移步《Gradle深度揭秘》
AutoBeanGradlePlugin on GitHub By openXu