如何較方便給上百張資料庫表新增表欄位

Linyb極客之路發表於2021-02-23

前言

年前和業務部門的研發小夥伴聊天,他說由於之前表設計考慮不周全,導致業務表缺少了一些欄位,他老大就把這個加表欄位的任務給他,咋一聽挺簡單的,不就加些欄位,但小夥伴煩惱的地方在於需要加這些欄位的表大概有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

相關文章