如何較方便給上百張數據庫表添加表字段


前言

年前和業務部門的研發小伙伴聊天,他說由於之前表設計考慮不周全,導致業務表缺少了一些字段,他老大就把這個加表字段的任務給他,咋一聽挺簡單的,不就加些字段,但小伙伴煩惱的地方在於需要加這些字段的表大概有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腳本的方式實現。說明下小伙伴遇到這種業務場景是發生在業務開發階段,而非生產環境。像這種大批量修改表結構的場景,是屬於架構上的嚴重失誤,理論上是不應該犯的。如果確實發生這種現象,得由DBA做好審計才能推到生產環境

demo鏈接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-generate-sqlscript


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM