版權聲明:本文爲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深度揭祕》