Files
yjearth/src/main/java/com/yj/earth/common/util/SQLiteUtil.java
2025-09-23 16:45:42 +08:00

369 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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> T queryForObject(String dbFilePath, String sql, List<Object> params, Class<T> clazz) throws SQLException, IllegalAccessException, InstantiationException {
List<T> results = queryForList(dbFilePath, sql, params, clazz);
return results.isEmpty() ? null : results.get(0);
}
/**
* 执行查询并返回对象列表
*/
public static <T> List<T> queryForList(String dbFilePath, String sql, List<Object> params, Class<T> clazz)
throws SQLException, IllegalAccessException, InstantiationException {
List<T> 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<Object> 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<Object> 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<Object> 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<Object> 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);
}
}