package com.yj.earth.common.convert; import java.nio.charset.StandardCharsets; import java.sql.*; import java.time.LocalDateTime; import java.util.Base64; public class MilitaryConverter { private static final String JDBC_DRIVER = "org.sqlite.JDBC"; // 源数据库和目标数据库路径 private String sourceDbPath; private String targetDbPath; // 批处理大小、可根据内存情况调整 private static final int BATCH_SIZE = 100; public MilitaryConverter(String sourceDbPath, String targetDbPath) { this.sourceDbPath = sourceDbPath; this.targetDbPath = targetDbPath; } public void convert() { Connection sourceConn = null; Connection targetConn = null; try { // 加载驱动 Class.forName(JDBC_DRIVER); // 连接源数据库和目标数据库 sourceConn = DriverManager.getConnection("jdbc:sqlite:" + sourceDbPath); targetConn = DriverManager.getConnection("jdbc:sqlite:" + targetDbPath); // 禁用自动提交、以便在出现错误时可以回滚 targetConn.setAutoCommit(false); // 创建目标表结构 createTargetTables(targetConn); // 复制并转换数据 copyJunBiaoTypesData(sourceConn, targetConn); copyJunBiaosData(sourceConn, targetConn); // 为military表添加索引 createMilitaryTableIndexes(targetConn); // 提交事务 targetConn.commit(); System.out.println("数据库转换成功!"); } catch (Exception e) { e.printStackTrace(); try { if (targetConn != null) { targetConn.rollback(); System.out.println("转换失败、已回滚操作!"); } } catch (SQLException ex) { ex.printStackTrace(); } } finally { // 关闭连接 try { if (sourceConn != null) sourceConn.close(); if (targetConn != null) targetConn.close(); } catch (SQLException e) { e.printStackTrace(); } } } private void createTargetTables(Connection conn) throws SQLException { System.out.println("开始创建目标表结构..."); Statement stmt = conn.createStatement(); // 创建military_type表 String sql = """ CREATE TABLE "military_type" ( "id" TEXT, "name" TEXT, "parent_id" TEXT, "tree_index" INTEGER, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; stmt.execute(sql); // 创建military表 sql = """ CREATE TABLE "military" ( "id" TEXT, "military_type_id" TEXT, "military_name" TEXT, "military_type" TEXT, "military_data" BLOB, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; stmt.execute(sql); stmt.close(); System.out.println("目标表结构创建完成"); } /** * 为 military 表创建索引 */ private void createMilitaryTableIndexes(Connection conn) throws SQLException { System.out.println("开始创建索引..."); Statement stmt = conn.createStatement(); String sql = """ CREATE INDEX idx_military_covering ON military( military_type_id, id, military_name, military_type, created_at, updated_at ); """; stmt.execute(sql); stmt.close(); System.out.println("military表索引创建完成"); } private int getTotalRecords(Connection conn, String tableName) throws SQLException { // 只统计未删除的数据 PreparedStatement stmt = conn.prepareStatement( "SELECT COUNT(*) AS total FROM " + tableName + " WHERE deleted_at IS NULL" ); ResultSet rs = stmt.executeQuery(); int total = rs.next() ? rs.getInt("total") : 0; rs.close(); stmt.close(); return total; } /** * 从jun_biao_types表复制数据到military_type表 */ private void copyJunBiaoTypesData(Connection sourceConn, Connection targetConn) throws SQLException { int totalRecords = getTotalRecords(sourceConn, "jun_biao_types"); System.out.println("开始转换 jun_biao_types 表数据、共" + totalRecords + "条记录"); // 查询未删除的类型数据 PreparedStatement sourceStmt = sourceConn.prepareStatement( "SELECT * FROM jun_biao_types WHERE deleted_at IS NULL" ); ResultSet rs = sourceStmt.executeQuery(); PreparedStatement targetStmt = targetConn.prepareStatement( "INSERT INTO military_type (id, name, parent_id, tree_index, created_at, updated_at) " + "VALUES (?, ?, ?, ?, ?, ?)" ); int count = 0; while (rs.next()) { targetStmt.setString(1, rs.getString("type_id")); targetStmt.setString(2, rs.getString("type_name")); if ("-1".equals(rs.getString("p_id"))) { targetStmt.setNull(3, Types.VARCHAR); } else { targetStmt.setString(3, rs.getString("p_id")); targetStmt.setString(3, rs.getString("p_id")); } targetStmt.setInt(4, 0); targetStmt.setObject(5, LocalDateTime.now()); targetStmt.setObject(6, LocalDateTime.now()); // 添加到批处理 targetStmt.addBatch(); count++; // 每达到批处理大小或最后一条记录时执行批处理 if (count % BATCH_SIZE == 0 || count == totalRecords) { targetStmt.executeBatch(); displayProgress(count, totalRecords, "jun_biao_types 表"); } } System.out.println("\n成功转换 jun_biao_types 表数据:" + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); } /** * 从jun_biaos表复制数据到military表 */ private void copyJunBiaosData(Connection sourceConn, Connection targetConn) throws SQLException { int totalRecords = getTotalRecords(sourceConn, "jun_biaos"); System.out.println("开始转换 jun_biaos 表数据、共" + totalRecords + "条记录"); // 对于大字段、使用向前滚动的结果集、避免缓存所有数据 PreparedStatement sourceStmt = sourceConn.prepareStatement( "SELECT * FROM jun_biaos WHERE deleted_at IS NULL", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY ); sourceStmt.setFetchSize(100); ResultSet rs = sourceStmt.executeQuery(); PreparedStatement targetStmt = targetConn.prepareStatement( "INSERT INTO military (id, military_type_id, military_name, military_type, military_data, " + "created_at, updated_at) " + "VALUES (?, ?, ?, ?, ?, ?, ?)" ); int count = 0; while (rs.next()) { targetStmt.setString(1, rs.getString("jun_biao_id")); // 关联ID对应 targetStmt.setString(2, rs.getString("p_id")); // 类型ID对应 targetStmt.setString(3, rs.getString("name")); // 名称对应 targetStmt.setString(4, "svg"); // 内容类型对应 byte[] dataBytes = rs.getBytes("data"); // 二进制数据对应 if (dataBytes != null) { targetStmt.setBytes(5, convertBase64ToPlainSvg(dataBytes)); } else { targetStmt.setNull(5, Types.BLOB); } targetStmt.setObject(6, LocalDateTime.now()); // 使用原始创建时间 targetStmt.setObject(7, LocalDateTime.now()); // 使用原始更新时间 // 添加到批处理 targetStmt.addBatch(); count++; // 执行批处理 if (count % BATCH_SIZE == 0 || count == totalRecords) { targetStmt.executeBatch(); displayProgress(count, totalRecords, "jun_biaos 表"); } } System.out.println("\n成功转换 jun_biaos 表数据:" + count + "条记录"); rs.close(); sourceStmt.close(); targetStmt.close(); } /** * 显示进度信息 */ private void displayProgress(int current, int total, String tableName) { double percentage = (double) current / total * 100; // 清除当前行并显示进度 System.out.printf("\r%s: 已完成 %.1f%% (%d/%d条)", tableName, percentage, current, total); } // Base64 SVG的前缀标识 private static final String SVG_BASE64_PREFIX = "data:image/svg+xml;base64,"; /** * 将Base64格式的SVG字节数组转换为明文SVG字节数组 * * @param base64SvgBytes 包含Base64编码的SVG字节数组(可带前缀) * @return 解码后的明文SVG字节数组 * @throws IllegalArgumentException 如果输入不是有效的Base64格式或为空 */ public static byte[] convertBase64ToPlainSvg(byte[] base64SvgBytes) { // 验证输入参数 if (base64SvgBytes == null || base64SvgBytes.length == 0) { throw new IllegalArgumentException("输入的Base64 SVG字节数组不能为空"); } // 将字节数组转换为字符串、处理可能存在的前缀 String base64SvgStr = new String(base64SvgBytes, StandardCharsets.UTF_8); String pureBase64Str = base64SvgStr; // 移除前缀(如果存在) if (base64SvgStr.startsWith(SVG_BASE64_PREFIX)) { pureBase64Str = base64SvgStr.substring(SVG_BASE64_PREFIX.length()); } try { // 执行Base64解码并返回字节数组 return Base64.getDecoder().decode(pureBase64Str); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("无效的Base64格式: " + e.getMessage(), e); } } public static void main(String[] args) { // 源数据库路径 String sourcePath = "F:\\YJEarth新.junbiao"; // 目标数据库路径 String targetPath = "F:\\通用军标库.junbiao"; System.out.println("开始数据库转换..."); System.out.println("源数据库: " + sourcePath); System.out.println("目标数据库: " + targetPath); long startTime = System.currentTimeMillis(); // 创建转换器并执行转换 MilitaryConverter converter = new MilitaryConverter(sourcePath, targetPath); converter.convert(); long endTime = System.currentTimeMillis(); double elapsedTime = (endTime - startTime) / 1000.0; System.out.printf("转换完成、耗时: %.2f秒%n", elapsedTime); } }