Jfinal+SQLite解决MYSQL迁移表未复制索引问题,完善迁移工具

发布于:2025-07-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

原mysql 表查询存在索引,

查询sql含force index(字段),SQLite不支持,会报错

解决办法,要么删除索引,要么强制执行索引 换成 INDEXED BY, 

String select sql="select * ";

String fromSql=" from tableName force index (START_TIME_TYPE) where 1=1

if (fromSql.contains("force index")){
   fromSql= fromSql.replaceAll("(?i)\\bFROM\\s+(\\w+)(\\s+\\w+)?\\s+force\\s+index\\s*\\(\\s*(\\w+)\\s*\\)",
         "FROM $1 $2 INDEXED BY $3");
}

这里就需要给sqlite数据库表添加对应索引,不然会报错SQLiteException: no such index: START_TIME_TYPE

生产数据库中,表多,索引比较多,在原先迁移表工具添加索引迁移,暂时未发现问题,需多多测试

package changeDataBase;

import java.sql.*;
import java.util.*;

public class MySQLToSQLiteMigration {
    private static final String MYSQL_URL = "jdbc:mysql://localhost:3306/database";
    private static final String MYSQL_USER = "admin";
    private static final String MYSQL_PASSWORD = "123456";
    private static final String SQLITE_URL = "jdbc:sqlite:D:/database/database.sqlite";

    public static void main(String[] args) {
        try {
            Class.forName("org.sqlite.JDBC");
        } catch (ClassNotFoundException var2) {
            System.err.println("SQLite JDBC driver not found!");
            var2.printStackTrace();
            return;
        }

        List<String> tableNames = getTableNamesFromMySQL();
        migrateTablesToSQLite(tableNames);
    }

    private static List<String> getTableNamesFromMySQL() {
        ArrayList<String> tableNames = new ArrayList<>();

        try (Connection conn = DriverManager.getConnection(MYSQL_URL, MYSQL_USER, MYSQL_PASSWORD)) {
            DatabaseMetaData metaData = conn.getMetaData();
            ResultSet rs = metaData.getTables(null, null, "%", new String[]{"TABLE"});

            while (rs.next()) {
                tableNames.add(rs.getString("TABLE_NAME"));
            }
        } catch (SQLException var6) {
            var6.printStackTrace();
        }

        return tableNames;
    }

    private static void migrateTablesToSQLite(List<String> tableNames) {
        try (Connection mysqlConn = DriverManager.getConnection(MYSQL_URL, MYSQL_USER, MYSQL_PASSWORD);
             Connection sqliteConn = DriverManager.getConnection(SQLITE_URL)) {

            Iterator<String> var3 = tableNames.iterator();

            while (var3.hasNext()) {
                String tableName = var3.next();
                System.out.println("Migrating table: " + tableName);
                migrateTable(mysqlConn, sqliteConn, tableName);
            }
        } catch (SQLException var9) {
            var9.printStackTrace();
        }
    }

    private static void migrateTable(Connection mysqlConn, Connection sqliteConn, String tableName) throws SQLException {
        String selectSql = "SELECT * FROM " + tableName;
        try (Statement stmt = mysqlConn.createStatement();
             ResultSet rs = stmt.executeQuery(selectSql)) {

            String createTableSql = getCreateTableSql(mysqlConn, tableName);
            try (Statement sqliteStmt = sqliteConn.createStatement()) {
                sqliteStmt.execute(createTableSql);
            }

            String insertSql = getInsertSql(rs.getMetaData(), tableName);
            try (PreparedStatement sqlitePstmt = sqliteConn.prepareStatement(insertSql)) {
                while (rs.next()) {
                    for (int i = 1; i <= rs.getMetaData().getColumnCount(); ++i) {
                        String columnName = rs.getMetaData().getColumnName(i);
                        String columnType = rs.getMetaData().getColumnTypeName(i);

                        // 特殊处理时间字段
                        if ("DATETIME".equalsIgnoreCase(columnType) || "TIMESTAMP".equalsIgnoreCase(columnType)) {
                            Object value = rs.getObject(i);
                            if (value instanceof java.sql.Timestamp) {
                                // 将 Timestamp 转换为标准日期时间格式
                                java.sql.Timestamp timestamp = (java.sql.Timestamp) value;
                                sqlitePstmt.setString(i, timestamp.toString());
                            } else {
                                sqlitePstmt.setObject(i, value);
                            }
                        } else {
                            sqlitePstmt.setObject(i, rs.getObject(i));
                        }
                    }
                    sqlitePstmt.addBatch();
                }
                sqlitePstmt.executeBatch();
            }
        }
 //处理索引
        migrateIndexes(mysqlConn, sqliteConn, tableName);
    }

    private static String getCreateTableSql(Connection mysqlConn, String tableName) throws SQLException {
        DatabaseMetaData metaData = mysqlConn.getMetaData();
        ResultSet rs = metaData.getColumns(null, null, tableName, "%");

        StringBuilder sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ");
        sb.append(tableName).append(" (");

        List<String> columns = new ArrayList<>();
        String primaryKey = null;

        while (rs.next()) {
            String columnName = rs.getString("COLUMN_NAME");
            String columnType = getSQLiteType(rs.getString("TYPE_NAME"));
            columns.add(columnName + " " + columnType);
        }

        // 获取主键信息
        String primaryKeySql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND CONSTRAINT_NAME = 'PRIMARY'";
        try (PreparedStatement pstmt = mysqlConn.prepareStatement(primaryKeySql)) {
            pstmt.setString(1, "database");
            pstmt.setString(2, tableName);
            try (ResultSet pkRs = pstmt.executeQuery()) {
                if (pkRs.next()) {
                    primaryKey = pkRs.getString("COLUMN_NAME");
                }
            }
        }

        sb.append(String.join(", ", columns));
        if (primaryKey != null) {
            sb.append(", PRIMARY KEY (").append(primaryKey).append(")");
        }

        sb.append(");");
        return sb.toString();
    }

    private static String getInsertSql(ResultSetMetaData metaData, String tableName) throws SQLException {
        StringBuilder sb = new StringBuilder("INSERT INTO ").append(tableName).append(" (");

        for (int i = 1; i <= metaData.getColumnCount(); ++i) {
            sb.append(metaData.getColumnName(i));
            if (i < metaData.getColumnCount()) {
                sb.append(", ");
            }
        }

        sb.append(") VALUES (");

        for (int i = 1; i <= metaData.getColumnCount(); ++i) {
            sb.append("?");
            if (i < metaData.getColumnCount()) {
                sb.append(", ");
            }
        }

        sb.append(");");
        return sb.toString();
    }

    private static String getSQLiteType(String mysqlType) {
        switch (mysqlType.toUpperCase()) {
            case "INT":
            case "INTEGER":
                return "INTEGER";
            case "VARCHAR":
            case "CHAR":
                return "TEXT";
            case "DATE":
                return "DATE";
            case "DATETIME":
            case "TIMESTAMP":
                return "DATETIME";
            case "DECIMAL":
                return "NUMERIC";
            case "FLOAT":
            case "DOUBLE":
                return "REAL";
            default:
                return "TEXT";
        }
    }


    //添加索引处理
    /**
     * 把 MySQL 表中除主键外的所有索引迁移到 SQLite
     */
    private static void migrateIndexes(Connection mysqlConn,
                                       Connection sqliteConn,
                                       String tableName) throws SQLException {

        // 1. 先查索引列
        String sql =
                "SELECT INDEX_NAME, NON_UNIQUE, COLUMN_NAME, SEQ_IN_INDEX " +
                        "FROM INFORMATION_SCHEMA.STATISTICS " +
                        "WHERE TABLE_SCHEMA = DATABASE() " +
                        "  AND TABLE_NAME   = ? " +
                        "  AND INDEX_NAME  != 'PRIMARY' " +
                        "ORDER BY INDEX_NAME, SEQ_IN_INDEX";

        // Map<索引名, 列列表>
        Map<String, List<String>> indexMap = new LinkedHashMap<>();
        try (PreparedStatement ps = mysqlConn.prepareStatement(sql)) {
            ps.setString(1, tableName);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    String idxName = rs.getString("INDEX_NAME");
                    indexMap.computeIfAbsent(idxName, k -> new ArrayList<>())
                            .add("\"" + rs.getString("COLUMN_NAME") + "\"");
                }
            }
        }

        // 2. 逐个在 SQLite 建索引
        for (Map.Entry<String, List<String>> e : indexMap.entrySet()) {
            String cols = String.join(", ", e.getValue());

            // 索引名在 SQLite 中保持同名,避免冲突可加前缀
            String createIdx = String.format(
                    "CREATE %s INDEX IF NOT EXISTS \"%s\" ON \"%s\" (%s);",
                    e.getKey().toUpperCase().startsWith("UNIQUE") ? "UNIQUE" : "",
                    e.getKey(), tableName, cols);

            try (Statement st = sqliteConn.createStatement()) {
                st.execute(createIdx);
                System.out.println("    " + createIdx.trim());
            }
        }
    }

}

网站公告

今日签到

点亮在社区的每一天
去签到