原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()); } } } }