package com.yj.earth.common.util; import java.sql.*; import java.util.*; import java.lang.reflect.*; import java.util.Date; import java.time.LocalDateTime; // 新增导入 import java.time.format.DateTimeFormatter; // 新增导入 import java.time.format.DateTimeParseException; // 新增导入 public class SQLiteUtil { // 加载 SQLite JDBC 驱动 static { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException e) { throw new RuntimeException("无法加载SQLite JDBC驱动", e); } } // 统一日期格式(匹配数据库TEXT字段存储的格式:yyyy-MM-dd'T'HH:mm:ss.SSS) private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; /** * 根据数据库文件绝对路径获取连接 */ public static Connection getConnection(String dbFilePath) throws SQLException { String url = "jdbc:sqlite:" + dbFilePath; return DriverManager.getConnection(url); } /** * 执行查询并返回单个对象 */ public static T queryForObject(String dbFilePath, String sql, List params, Class clazz) throws SQLException, IllegalAccessException, InstantiationException { List results = queryForList(dbFilePath, sql, params, clazz); return results.isEmpty() ? null : results.get(0); } /** * 执行查询并返回对象列表 */ public static List queryForList(String dbFilePath, String sql, List params, Class clazz) throws SQLException, IllegalAccessException, InstantiationException { List resultList = new ArrayList<>(); // 使用try-with-resources确保资源自动关闭 try (Connection conn = getConnection(dbFilePath); PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { // 执行查询 try (ResultSet rs = pstmt.executeQuery()) { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); // 处理结果集 while (rs.next()) { T obj = clazz.newInstance(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnName(i); Object value = rs.getObject(i); // 设置对象属性(自动处理类型转换) setFieldValue(obj, columnName, value); } resultList.add(obj); } } } return resultList; } /** * 执行增删改SQL语句 */ public static int executeUpdate(String dbFilePath, String sql, List params) throws SQLException { try (Connection conn = getConnection(dbFilePath); PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { return pstmt.executeUpdate(); } } /** * 创建并设置参数化的PreparedStatement(关键:处理LocalDateTime转String) */ private static PreparedStatement createPreparedStatement(Connection conn, String sql, List params) throws SQLException { PreparedStatement pstmt = conn.prepareStatement(sql); if (params != null && !params.isEmpty()) { int index = 1; for (Object value : params) { // 新增:LocalDateTime类型转为String、适配SQLite的TEXT字段 if (value instanceof LocalDateTime) { String dateStr = ((LocalDateTime) value).format(LOCAL_DATE_TIME_FORMATTER); pstmt.setObject(index++, dateStr); } // 新增:byte[]类型显式指定为BLOB(避免SQLite自动转换异常) else if (value instanceof byte[]) { pstmt.setBytes(index++, (byte[]) value); } else { pstmt.setObject(index++, value); } } } return pstmt; } /** * 通过反射设置对象的字段值 */ private static void setFieldValue(Object obj, String columnName, Object value) { try { Field field = findField(obj.getClass(), columnName); if (field != null) { field.setAccessible(true); Object convertedValue = convertValue(field.getType(), value); field.set(obj, convertedValue); } } catch (IllegalAccessException e) { System.err.println("警告: 无法设置字段 " + columnName + " 的值 - " + e.getMessage()); } } /** * 查找字段、支持下划线命名转驼峰命名(如created_at → createdAt) */ private static Field findField(Class clazz, String columnName) { // 1. 直接匹配字段名(如数据库列名与字段名一致) try { return clazz.getDeclaredField(columnName); } catch (NoSuchFieldException e) { // 2. 下划线转驼峰后匹配(如created_at → createdAt) String camelCaseName = underscoreToCamelCase(columnName); try { return clazz.getDeclaredField(camelCaseName); } catch (NoSuchFieldException e1) { // 3. 递归查找父类(支持继承场景) if (clazz.getSuperclass() != null) { return findField(clazz.getSuperclass(), columnName); } return null; } } } /** * 下划线命名转驼峰命名(工具方法) */ private static String underscoreToCamelCase(String str) { if (str == null || str.isEmpty()) { return str; } StringBuilder sb = new StringBuilder(); boolean nextUpperCase = false; for (char c : str.toCharArray()) { if (c == '_') { nextUpperCase = true; } else { if (nextUpperCase) { sb.append(Character.toUpperCase(c)); nextUpperCase = false; } else { // 修正:首字母小写(如CREATED_AT → createdAt、而非CreatedAt) sb.append(Character.toLowerCase(c)); } } } return sb.toString(); } /** * 核心:类型转换(新增LocalDateTime解析、优化BLOB适配) */ private static Object convertValue(Class targetType, Object value) { if (value == null) { return null; } // 类型已匹配、直接返回 if (targetType.isInstance(value)) { return value; } // 1. 数字类型转换(int/long/double等) if (targetType == Integer.class || targetType == int.class) { return ((Number) value).intValue(); } else if (targetType == Long.class || targetType == long.class) { return ((Number) value).longValue(); } else if (targetType == Double.class || targetType == double.class) { return ((Number) value).doubleValue(); } else if (targetType == Float.class || targetType == float.class) { return ((Number) value).floatValue(); } // 2. 布尔类型转换(支持数字/字符串转布尔) else if (targetType == Boolean.class || targetType == boolean.class) { if (value instanceof Number) { return ((Number) value).intValue() != 0; } else if (value instanceof String) { return "true".equalsIgnoreCase((String) value); } } // 3. 字符串类型转换(所有类型转String) else if (targetType == String.class) { return value.toString(); } // 4. 日期类型转换(java.util.Date) else if (targetType == Date.class) { if (value instanceof Timestamp) { return new Date(((Timestamp) value).getTime()); } else if (value instanceof LocalDateTime) { // 支持LocalDateTime转Date(如需) return Date.from(((LocalDateTime) value).atZone(java.time.ZoneId.systemDefault()).toInstant()); } } // 5. 新增:LocalDateTime类型转换(SQLite TEXT → Java LocalDateTime) else if (targetType == LocalDateTime.class) { if (value instanceof String) { try { // 解析数据库存储的ISO格式字符串(如"2025-09-18T17:30:27.143") return LocalDateTime.parse((String) value, LOCAL_DATE_TIME_FORMATTER); } catch (DateTimeParseException e) { System.err.println("警告: 日期解析失败、字符串=" + value + "、格式应为yyyy-MM-dd'T'HH:mm:ss.SSS - " + e.getMessage()); return null; } } else if (value instanceof Timestamp) { // 兼容Timestamp类型(如其他数据库迁移场景) return ((Timestamp) value).toLocalDateTime(); } } // 6. 新增:byte[]类型转换(SQLite BLOB → Java byte[]) else if (targetType == byte[].class && value instanceof Blob) { Blob blob = (Blob) value; try { return blob.getBytes(1, (int) blob.length()); } catch (SQLException e) { System.err.println("警告: BLOB转byte[]失败 - " + e.getMessage()); return null; } } // 无法转换时返回原始值(避免崩溃、打印警告) System.err.println("警告: 不支持的类型转换、目标类型=" + targetType.getName() + "、原始值类型=" + value.getClass().getName()); return value; } /** * 执行DDL语句(CREATE, ALTER, DROP等) */ private static void executeDDL(String dbFilePath, String sql, List params) throws SQLException { try (Connection conn = getConnection(dbFilePath); PreparedStatement pstmt = createPreparedStatement(conn, sql, params)) { pstmt.execute(); } } /** * 执行无参数的DDL语句 */ public static void executeDDL(String dbFilePath, String sql) { try { executeDDL(dbFilePath, sql, null); } catch (SQLException e) { throw new RuntimeException("执行DDL语句失败、SQL=" + sql, e); } } /** * 执行查询并返回count结果 */ public static int queryForCount(String dbFilePath, String sql, List params) throws SQLException { try (Connection conn = getConnection(dbFilePath); PreparedStatement pstmt = createPreparedStatement(conn, sql, params); ResultSet rs = pstmt.executeQuery()) { if (rs.next()) { return rs.getInt(1); } return 0; } } /** * 执行无参数的查询并返回count结果 */ public static int queryForCount(String dbFilePath, String sql) throws SQLException { return queryForCount(dbFilePath, sql, null); } /** * 初始化数据库表 */ public static void initializationModel(String modelPath) { // 创建模型类型表 String sql = """ CREATE TABLE "model_type" ( "id" TEXT, "name" TEXT, "parent_id" TEXT, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; executeDDL(modelPath, sql); // 创建模型表 sql = """ CREATE TABLE "model" ( "id" TEXT, "model_type_id" TEXT, "model_name" TEXT, "model_type" TEXT, "poster_type" TEXT, "poster" TEXT, "data" TEXT, "view" TEXT, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; executeDDL(modelPath, sql); } /** * 初始化数据库表 */ public static void initializationMilitary(String modelPath) { // 创建军标类型表 String sql = """ CREATE TABLE "military_type" ( "id" TEXT, "name" TEXT, "parent_id" TEXT, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; executeDDL(modelPath, sql); // 创建军标表 sql = """ CREATE TABLE "military" ( "id" TEXT, "military_type_id" TEXT, "military_name" TEXT, "military_type" TEXT, "data" TEXT, "view" TEXT, "created_at" TEXT, "updated_at" TEXT, PRIMARY KEY ("id") ); """; executeDDL(modelPath, sql); } }