前言
年前和業務部門的研發小夥伴聊天,他說由於之前表設計考慮不周全,導致業務表缺少了一些欄位,他老大就把這個加表欄位的任務給他,咋一聽挺簡單的,不就加些欄位,但小夥伴煩惱的地方在於需要加這些欄位的表大概有100多張,如果單靠手動新增,那效率太低了。於是他問我有沒有啥方法,能比較方便的實現這個需求,今天就來水一下這個話題
需求分析
小夥伴的訴求在於方便的實現,那怎麼實現方便這個訴求。答案的本質就是這個實現不要讓他自己做,讓別人或者其他東西實現他訴求
方案思路
方案一: 把這個需求安排給其他人做,哈哈
方案二:寫儲存過程或者函式
方案三:通過寫sql指令碼執行
本文重點講解方案三寫sql指令碼執行
如何實現這個sql指令碼
實現這個sql指令碼的方式有很多種,本文就講下利用java程式生成這個sql指令碼。生成這個sql指令碼的關鍵在於DatabaseMetaData這個類,這個類是啥?它是java.sql包中的類,利用它可以獲取我們連線到的資料庫的結構、儲存等很多資訊
其更多詳細介紹可以檢視如下連結
https://www.apiref.com/java11-zh/java.sql/java/sql/DatabaseMetaData.html
核心程式碼實現
1、獲取資料庫下的所有資料庫表名
/**
* 獲取資料庫下的所有資料庫表名
*
* @return Map<String, List<String>> key為資料庫名稱,value為該資料庫下的所有表名
*/
public Map<String, List<String>> getDbTablesNamesMap(){
Map<String, List<String>> dbTableNamesMap = new HashMap<>();
try {
//獲取資料庫的後設資料
DatabaseMetaData dbMetaData = getConnection().getMetaData();
//從後設資料中獲取到所有的表名
ResultSet rs = dbMetaData.getTables(null, null, null,new String[] { "TABLE" });
List<String> tableNames;
while(rs.next()) {
String tableName = rs.getString("TABLE_NAME");
String curTableDbName = rs.getString("TABLE_CAT");
String tableNameType = rs.getString("TABLE_TYPE");
//表模式(可能為空),在oracle中獲取的是名稱空間
String tableNameSchema = rs.getString("TABLE_SCHEM");
String tableNameRemark = rs.getString("REMARKS");
System.out.println("表名: " + tableName + ",表所屬資料庫: " + curTableDbName + ",表型別: " + tableNameType + ",表模式: " + tableNameSchema + ",表備註: " + tableNameRemark);
//跳過mysql自帶的系統庫
if("sys".equalsIgnoreCase(curTableDbName)){
continue;
}
if(dbTableNamesMap.containsKey(curTableDbName)){
tableNames = dbTableNamesMap.get(curTableDbName);
}else{
tableNames = new ArrayList<>();
}
tableNames.add(tableName);
dbTableNamesMap.put(curTableDbName,tableNames);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
close();
return dbTableNamesMap;
}
2、拼湊要執行的sql語句
public static List<String> prepareSqlStatementWithStringFormat(boolean isForFiles) {
String sql = "alter table %s ADD COLUMN create_time DATETIME COMMENT '建立時間',ADD COLUMN created_by_id BIGINT(20) DEFAULT NULL COMMENT '建立人id', ADD COLUMN update_time DATETIME COMMENT '修改時間', ADD COLUMN last_updated_by_id BIGINT(20) DEFAULT NULL COMMENT '修改人id'";
List<String> sqlStatements = new ArrayList<>();
Map<String, List<String>> dbTableNamesMap = DbUtils.INSTANCE.getDbTablesNamesMap();
dbTableNamesMap.forEach((dbName,tableNames) -> {
for (String tableName : tableNames) {
String record = dbName + "." + tableName;
String sqlStatement = String.format(sql, record);
//如果是要寫入檔案,則每條生成的sql語句,需追加分號
if(isForFiles){
sqlStatement = sqlStatement + ";";
}
System.out.println(sqlStatement);
sqlStatements.add(sqlStatement);
}
});
return sqlStatements;
}
3、將生成的sql語句寫入檔案
public static void writeSqlStatement2File(List<String> sqlStatements,String filePath){
try {
File file = new File(filePath);
if(!file.exists()){
file.createNewFile();
}
FileUtil.writeUtf8Lines(sqlStatements,file);
System.out.println("資料庫指令碼寫入"+filePath+"成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println("資料庫指令碼寫入"+filePath+"失敗");
}
}
總結
小夥伴並沒有採用方案三的實現方法,而是採取了方案一,通過一頓飯的代價,讓我幫他實現了這個需求。最後我幫他實現整體方案是以flyway+java程式實現的sql指令碼的方式實現
demo連結
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-generate-sqlscript