Java tool class: SqlFileCompareUtils (compare database table and field changes)

【Background】

In the actual development process, we usually have a development environment, a test environment, and a production environment.

In these environments, there are generally corresponding databases. Since each new requirement is tested in the test environment, the database structure of different environments will be different.

When the functions of the new version are tested in the test environment, the new functions need to be updated to the production environment. At this time, the database structure of the production environment needs to be consistent with the database structure of the test environment again to ensure the normal use of the new functions.

【Database comparison】

When we want to know the changes in the two databases, if we compare each table and each field, when the changes are large, this will be a very unfriendly experience, not only time-consuming and labor-intensive, And it's error-prone. So I wrote a tool class SqlFileCompareUtils for database field comparison (see the source code at the end).

【Instructions】

(1) First export the sql file (only the structure) of the database of each environment;

 (2) Then put each exported sql file in the root directory of any project.

 (3) Then open the tool class SqlFileCompareUtils (see the source code at the end), and modify the two constants in the tool class to the sql file names of the corresponding environment.

 (4) In the main aspect of the execution class, the console will print that the database structure has changed, and the output is as follows (sample), so that you can clearly add the corresponding tables and fields to the production environment according to the following prompts.

There are 17 changes in total, as follows:
[1] New field in z_dept table: fillStatus : smallint(2)
[2] New field in z_dept table: syncStatus : smallint(6)
[3] New field in z_dept_month_labor table: transProject : char (1)
[4] New fields in the z_dept_month_labor table: transDept : char(1)
[5] Changes in the fields of the z_project table: piCode : varchar(255) -> varchar(50)
[6] Changes in the fields in the z_project table: code : varchar( 255) -> varchar(50)
[7] z_project table field changes: operateSn : varchar(255) -> varchar(50)
[8] z_project table field changes: finName : varchar(255) -> varchar(50)
[9 ] z_project table field changes: finCode : varchar(255) -> varchar(50)
[10] z_project table field changes: name : varchar(255) -> varchar(50)
[11] z_project table field changes: operateName : varchar( 255) -> varchar(50)
[12] z_project table field changes: syncStatus : varchar(255) -> varchar(5)
[13] New field in z_project table: isTransfer: char(1)
[14] New table: z_project_log
[15] New table: z_sow_log
[16] New table: z_dept_user
[17] New table: z_sow

[tool source code]

package com.zyq.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 比较两个SQL文件,是否存在表变化和字段变化
 * 
 * @author zyq
 * @since 2021/11/04
 */
public class SqlFileCompareUtils {

    // 源 sql 文件(测试环境)
    public static String sqlFileNameFrom = "zyq_test.sql";
    // 目标 sql 文件(正式环境)
    public static String sqlFileNameTo = "zyq_live.sql";

    public static void main(String[] args) {
        printDifference();
    }

    public static void printDifference() {
        String msg = checkFile();
        if (msg != null) {
            System.err.println(msg);
            return;
        }
        // 获取源 sql 文件的表字段信息
        Map<String, Map<String, String>> fromMap = getTableMap(sqlFileNameFrom);
        // 获取目标 sql 文件的表字段信息
        Map<String, Map<String, String>> toMap = getTableMap(sqlFileNameTo);
        // 比较两个 sql 文件的字段信息
        List<String> differenceList = getDifferenceList(fromMap, toMap);
        if (differenceList.isEmpty()) {
            System.out.println("表和字段无变化");
            return;
        }
        int size = differenceList.size();
        System.out.println("共变化 " + size + "处,具体如下:");
        for (int i = 0; i < size; i++) {
            System.out.println(String.format("【%s】%s", (i + 1), differenceList.get(i)));
        }
    }

    private static List<String> getDifferenceList(Map<String, Map<String, String>> fromMap,
            Map<String, Map<String, String>> toMap) {
        List<String> list = new ArrayList<String>();
        Set<String> toTableSet = toMap.keySet();
        for (String tableName : toTableSet) {
            Map<String, String> fromFieldMap = fromMap.get(tableName);
            // 如果目标文件有该表,源文件没有该表,则表已被删除,冗余
            if (fromFieldMap == null) {
                list.add("多余表:" + tableName);
            }
            // 继续比较表的字段
            Map<String, String> toFieldMap = toMap.get(tableName);
            List<String> fieldList = getDifferenceFieldList(tableName, fromFieldMap, toFieldMap);
            if (!fieldList.isEmpty()) {
                list.addAll(fieldList);
            }
        }
        // 新增表
        Set<String> fromTableSet = fromMap.keySet();
        fromTableSet.removeAll(toTableSet);
        for (String tableName : fromTableSet) {
            list.add("新增表:" + tableName);
        }
        return list;
    }

    private static List<String> getDifferenceFieldList(String tableName, Map<String, String> fromMap,
            Map<String, String> toMap) {
        List<String> list = new ArrayList<String>();
        Set<String> toFiledSet = toMap.keySet();
        for (String filedName : toFiledSet) {
            String fromFileType = fromMap.get(filedName);
            // 如果目标文件表有该字段,源文件表没有该字段,则该字段已被删除,冗余
            if (fromFileType == null) {
                list.add("%s 表多余字段:" + fromFileType);
                continue;
            }
            // 继续比较字段类型
            String toFileType = toMap.get(filedName);
            if (!fromFileType.equals(toFileType)) {
                list.add(String.format("%s 表字段变化:%s : %s -> %s", tableName, filedName, toFileType, fromFileType));
            }
        }
        // 新增字段
        Set<String> fromFiledSet = fromMap.keySet();
        fromFiledSet.removeAll(toFiledSet);
        for (String fromFiledName : fromFiledSet) {
            list.add(String.format("%s 表新增字段:%s : %s", tableName, fromFiledName, fromMap.get(fromFiledName)));
        }
        return list;
    }

    private static Map<String, Map<String, String>> getTableMap(String fileName) {
        String sqlFrom = getFileContent(fileName);
        Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
        // 读取 sql 文件的所有行
        String[] lines = sqlFrom.split("\r\n");
        String tableName = null;
        for (String line : lines) {
            String trimLine = line.trim();
            // 以【CREATE TABLE】为开头的,则为表名
            if (trimLine.startsWith("CREATE TABLE")) {
                // 然后去掉 CREATE TABLE、`、( 即可获取表名
                tableName = trimLine.replaceAll("CREATE TABLE|`|\\(", "").trim();
                map.put(tableName, new HashMap<String, String>());
                continue;
            }
            // 以 ` 开头的,则为字段
            if (trimLine.startsWith("`")) {
                // 第 1 个 ` 和第 2 个 ` 之间的则为字段名
                String fieldName = getSplintIndexValue(trimLine, "`", 1);
                // 按 [空格] 拆分的第2个为字段类型
                String fieldType = getSplintIndexValue(trimLine, " ", 2);
                map.get(tableName).put(fieldName, fieldType);
            }
        }
        return map;
    }

    private static String getSplintIndexValue(String s, String regex, int index) {
        String[] strs = s.split(regex);
        int idx = 0;
        for (String str : strs) {
            if (!str.isEmpty()) {
                idx++;
            }
            if (idx == index) {
                return str;
            }
        }
        return "";
    }

    private static boolean isEmpty(String s) {
        return s == null || s.isEmpty();
    }

    private static String checkFile() {
        if (isEmpty(sqlFileNameFrom)) {
            return "源文件名称不能为空";
        }
        if (!sqlFileNameFrom.endsWith(".sql")) {
            return "源文件不是sql文件";
        }
        File from = new File(sqlFileNameFrom);
        if (!from.exists()) {
            return "源文件不存在于工程根目录下";
        }
        if (isEmpty(sqlFileNameTo)) {
            return "目标文件名称不能为空";
        }
        if (!sqlFileNameTo.endsWith(".sql")) {
            return "目标文件不是sql文件";
        }
        File to = new File(sqlFileNameTo);
        if (!to.exists()) {
            return "目标文件不存在于工程根目录下";
        }
        return null;
    }

    private static String getFileContent(String fileName) {
        File file = new File(fileName);
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] b = new byte[(int) file.length()];
            fis.read(b);
            return new String(b, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

}

Guess you like

Origin blog.csdn.net/sunnyzyq/article/details/121142302