2025-10-09 11:03:15 +08:00
|
|
|
|
package com.yj.earth.common.convert;
|
2025-09-29 17:34:21 +08:00
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
import java.nio.charset.StandardCharsets;
|
2025-09-29 13:56:36 +08:00
|
|
|
|
import java.sql.*;
|
2025-09-29 17:34:21 +08:00
|
|
|
|
import java.time.LocalDateTime;
|
2025-10-09 11:03:15 +08:00
|
|
|
|
import java.util.Base64;
|
2025-09-29 13:56:36 +08:00
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
public class MilitaryConverter {
|
2025-09-29 13:56:36 +08:00
|
|
|
|
private static final String JDBC_DRIVER = "org.sqlite.JDBC";
|
2025-09-29 17:34:21 +08:00
|
|
|
|
// 源数据库和目标数据库路径
|
|
|
|
|
|
private String sourceDbPath;
|
|
|
|
|
|
private String targetDbPath;
|
|
|
|
|
|
// 批处理大小、可根据内存情况调整
|
|
|
|
|
|
private static final int BATCH_SIZE = 100;
|
|
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
public MilitaryConverter(String sourceDbPath, String targetDbPath) {
|
2025-09-29 17:34:21 +08:00
|
|
|
|
this.sourceDbPath = sourceDbPath;
|
|
|
|
|
|
this.targetDbPath = targetDbPath;
|
|
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
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);
|
|
|
|
|
|
// 复制并转换数据
|
2025-10-09 11:03:15 +08:00
|
|
|
|
copyJunBiaoTypesData(sourceConn, targetConn);
|
|
|
|
|
|
copyJunBiaosData(sourceConn, targetConn);
|
|
|
|
|
|
// 为military表添加索引
|
|
|
|
|
|
createMilitaryTableIndexes(targetConn);
|
2025-09-29 17:34:21 +08:00
|
|
|
|
// 提交事务
|
|
|
|
|
|
targetConn.commit();
|
|
|
|
|
|
System.out.println("数据库转换成功!");
|
|
|
|
|
|
} catch (Exception e) {
|
2025-09-29 13:56:36 +08:00
|
|
|
|
e.printStackTrace();
|
2025-09-29 17:34:21 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
private void createTargetTables(Connection conn) throws SQLException {
|
|
|
|
|
|
System.out.println("开始创建目标表结构...");
|
|
|
|
|
|
Statement stmt = conn.createStatement();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
// 创建military_type表
|
2025-09-29 17:34:21 +08:00
|
|
|
|
String sql = """
|
2025-10-09 11:03:15 +08:00
|
|
|
|
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" (
|
2025-09-29 17:34:21 +08:00
|
|
|
|
"id" TEXT,
|
2025-10-09 11:03:15 +08:00
|
|
|
|
"military_type_id" TEXT,
|
|
|
|
|
|
"military_name" TEXT,
|
|
|
|
|
|
"military_type" TEXT,
|
|
|
|
|
|
"military_data" BLOB,
|
2025-09-29 17:34:21 +08:00
|
|
|
|
"created_at" TEXT,
|
|
|
|
|
|
"updated_at" TEXT,
|
|
|
|
|
|
PRIMARY KEY ("id")
|
|
|
|
|
|
);
|
|
|
|
|
|
""";
|
|
|
|
|
|
stmt.execute(sql);
|
|
|
|
|
|
stmt.close();
|
|
|
|
|
|
System.out.println("目标表结构创建完成");
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-10-09 11:03:15 +08:00
|
|
|
|
* 为 military 表创建索引
|
2025-09-29 13:56:36 +08:00
|
|
|
|
*/
|
2025-10-09 11:03:15 +08:00
|
|
|
|
private void createMilitaryTableIndexes(Connection conn) throws SQLException {
|
|
|
|
|
|
System.out.println("开始创建索引...");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
Statement stmt = conn.createStatement();
|
|
|
|
|
|
|
|
|
|
|
|
String sql = """
|
2025-10-09 11:03:15 +08:00
|
|
|
|
CREATE INDEX idx_military_covering ON military(
|
|
|
|
|
|
military_type_id,
|
2025-09-29 17:34:21 +08:00
|
|
|
|
id,
|
2025-10-09 11:03:15 +08:00
|
|
|
|
military_name,
|
|
|
|
|
|
military_type,
|
2025-09-29 17:34:21 +08:00
|
|
|
|
created_at,
|
|
|
|
|
|
updated_at
|
|
|
|
|
|
);
|
|
|
|
|
|
""";
|
|
|
|
|
|
stmt.execute(sql);
|
|
|
|
|
|
|
|
|
|
|
|
stmt.close();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
System.out.println("military表索引创建完成");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
private int getTotalRecords(Connection conn, String tableName) throws SQLException {
|
2025-10-09 11:03:15 +08:00
|
|
|
|
// 只统计未删除的数据
|
|
|
|
|
|
PreparedStatement stmt = conn.prepareStatement(
|
|
|
|
|
|
"SELECT COUNT(*) AS total FROM " + tableName + " WHERE deleted_at IS NULL"
|
|
|
|
|
|
);
|
2025-09-29 17:34:21 +08:00
|
|
|
|
ResultSet rs = stmt.executeQuery();
|
|
|
|
|
|
int total = rs.next() ? rs.getInt("total") : 0;
|
|
|
|
|
|
rs.close();
|
|
|
|
|
|
stmt.close();
|
|
|
|
|
|
return total;
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从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"
|
|
|
|
|
|
);
|
2025-09-29 17:34:21 +08:00
|
|
|
|
ResultSet rs = sourceStmt.executeQuery();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
PreparedStatement targetStmt = targetConn.prepareStatement(
|
2025-10-09 11:03:15 +08:00
|
|
|
|
"INSERT INTO military_type (id, name, parent_id, tree_index, created_at, updated_at) " +
|
2025-09-29 17:34:21 +08:00
|
|
|
|
"VALUES (?, ?, ?, ?, ?, ?)"
|
|
|
|
|
|
);
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while (rs.next()) {
|
|
|
|
|
|
targetStmt.setString(1, rs.getString("type_id"));
|
|
|
|
|
|
targetStmt.setString(2, rs.getString("type_name"));
|
2025-10-09 11:03:15 +08:00
|
|
|
|
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"));
|
|
|
|
|
|
}
|
2025-09-29 17:34:21 +08:00
|
|
|
|
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();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
displayProgress(count, totalRecords, "jun_biao_types 表");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
System.out.println("\n成功转换 jun_biao_types 表数据:" + count + "条记录");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
rs.close();
|
|
|
|
|
|
sourceStmt.close();
|
|
|
|
|
|
targetStmt.close();
|
|
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从jun_biaos表复制数据到military表
|
|
|
|
|
|
*/
|
|
|
|
|
|
private void copyJunBiaosData(Connection sourceConn, Connection targetConn) throws SQLException {
|
|
|
|
|
|
int totalRecords = getTotalRecords(sourceConn, "jun_biaos");
|
|
|
|
|
|
System.out.println("开始转换 jun_biaos 表数据、共" + totalRecords + "条记录");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 对于大字段、使用向前滚动的结果集、避免缓存所有数据
|
|
|
|
|
|
PreparedStatement sourceStmt = sourceConn.prepareStatement(
|
2025-10-09 11:03:15 +08:00
|
|
|
|
"SELECT * FROM jun_biaos WHERE deleted_at IS NULL",
|
2025-09-29 17:34:21 +08:00
|
|
|
|
ResultSet.TYPE_FORWARD_ONLY,
|
|
|
|
|
|
ResultSet.CONCUR_READ_ONLY
|
|
|
|
|
|
);
|
|
|
|
|
|
sourceStmt.setFetchSize(100);
|
|
|
|
|
|
ResultSet rs = sourceStmt.executeQuery();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
PreparedStatement targetStmt = targetConn.prepareStatement(
|
2025-10-09 11:03:15 +08:00
|
|
|
|
"INSERT INTO military (id, military_type_id, military_name, military_type, military_data, " +
|
|
|
|
|
|
"created_at, updated_at) " +
|
|
|
|
|
|
"VALUES (?, ?, ?, ?, ?, ?, ?)"
|
2025-09-29 17:34:21 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
while (rs.next()) {
|
2025-10-09 11:03:15 +08:00
|
|
|
|
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"); // 二进制数据对应
|
2025-09-29 17:34:21 +08:00
|
|
|
|
if (dataBytes != null) {
|
2025-10-09 11:03:15 +08:00
|
|
|
|
targetStmt.setBytes(5, convertBase64ToPlainSvg(dataBytes));
|
2025-09-29 17:34:21 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
targetStmt.setNull(5, Types.BLOB);
|
|
|
|
|
|
}
|
2025-10-09 11:03:15 +08:00
|
|
|
|
targetStmt.setObject(6, LocalDateTime.now()); // 使用原始创建时间
|
|
|
|
|
|
targetStmt.setObject(7, LocalDateTime.now()); // 使用原始更新时间
|
2025-09-29 17:34:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加到批处理
|
|
|
|
|
|
targetStmt.addBatch();
|
|
|
|
|
|
count++;
|
|
|
|
|
|
// 执行批处理
|
|
|
|
|
|
if (count % BATCH_SIZE == 0 || count == totalRecords) {
|
|
|
|
|
|
targetStmt.executeBatch();
|
2025-10-09 11:03:15 +08:00
|
|
|
|
displayProgress(count, totalRecords, "jun_biaos 表");
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-29 17:34:21 +08:00
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
System.out.println("\n成功转换 jun_biaos 表数据:" + count + "条记录");
|
2025-09-29 17:34:21 +08:00
|
|
|
|
rs.close();
|
|
|
|
|
|
sourceStmt.close();
|
|
|
|
|
|
targetStmt.close();
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-29 17:34:21 +08:00
|
|
|
|
* 显示进度信息
|
2025-09-29 13:56:36 +08:00
|
|
|
|
*/
|
2025-09-29 17:34:21 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2025-09-29 13:56:36 +08:00
|
|
|
|
|
2025-10-09 11:03:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 17:34:21 +08:00
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
// 源数据库路径
|
2025-10-09 11:03:15 +08:00
|
|
|
|
String sourcePath = "F:\\YJEarth新.junbiao";
|
2025-09-29 17:34:21 +08:00
|
|
|
|
// 目标数据库路径
|
2025-10-09 11:03:15 +08:00
|
|
|
|
String targetPath = "F:\\通用军标库.junbiao";
|
2025-09-29 17:34:21 +08:00
|
|
|
|
System.out.println("开始数据库转换...");
|
|
|
|
|
|
System.out.println("源数据库: " + sourcePath);
|
|
|
|
|
|
System.out.println("目标数据库: " + targetPath);
|
|
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
|
|
// 创建转换器并执行转换
|
2025-10-09 11:03:15 +08:00
|
|
|
|
MilitaryConverter converter = new MilitaryConverter(sourcePath, targetPath);
|
2025-09-29 17:34:21 +08:00
|
|
|
|
converter.convert();
|
|
|
|
|
|
long endTime = System.currentTimeMillis();
|
|
|
|
|
|
double elapsedTime = (endTime - startTime) / 1000.0;
|
|
|
|
|
|
System.out.printf("转换完成、耗时: %.2f秒%n", elapsedTime);
|
2025-09-29 13:56:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|