Harmony2.2.0通过RdbStore实现通用类操作本地SQLite数据库

硬件设备:鸿蒙平板电脑

开发环境: harmonyOS 2.2.0

API 6

DevEco 3.1

开发架构:xml + java

harmonyOS 2.2.0可以结合RdbStore与RdbPredicates实现对本地数据库SQLite的操作,根据其技术特性可以封装为DatabaseManager类与RdbUtils类,在APP启动时进行初始化,并在需要时进行相应的增删改查。

DatabaseManager

MyApplication 是整个应用的 全局生命周期管理类,在应用启动时最先执行,在所有 Ability、AbilitySlice 之前就已经创建。所以可以在该类中进行初始化数据库日志系统、网络配置、SDK 等。比如你需要在应用启动时初始化一些数据表,用于本地数据的存储,可以实现一个DatabaseManager类方便管理。代码如下:

java 复制代码
public class DatabaseManager {

    private final Context context;
    private RdbStore store;
    private static DatabaseManager instance;

    public DatabaseManager(Context context) {
        this.context = context;
    }

    /**
     * 获取 RdbStore 对象,方便执行查询操作
     */
    public RdbStore getStore() {
        return store;
    }

    public static synchronized DatabaseManager getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseManager(context);
        }
        return instance;
    }

    public void initDatabaseIfNeeded() {
        try {
            HiLog.info(LABEL,"initDatabaseIfNeeded函数执行");
            DatabaseHelper helper = new DatabaseHelper(context);
            StoreConfig config = StoreConfig.newDefaultConfig("xxx.db");

            store = helper.getRdbStore(config, 1, new RdbOpenCallback() {
                @Override
                public void onCreate(RdbStore store) {
                    // 判断user表是否已经存在
                    if (isTableExists("user")) {
                        HiLog.info(LABEL,"有表了,直接返回,不重新导入");
                        return;
                    }
                    // 数据库首次创建触发
                    // HiLog.info(LABEL,"数据库首次创建触发");
                    try{
                        // 没有表,执行 SQL 文件导入
                        HiLog.info(LABEL,"执行sql文件导入");
                        InputStream input = context.getResourceManager()
                                .getRawFileEntry("resources/rawfile/xxx.sql")
                                .openRawFile();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(input));

                        String line;
                        StringBuilder sqlBuilder = new StringBuilder();

                        while ((line = reader.readLine()) != null) {
                            line = line.trim();
                            if (line.isEmpty() || line.startsWith("--")) continue;

                            sqlBuilder.append(line).append(" ");

                            if (line.endsWith(";")) {
                                String sql = sqlBuilder.toString();
                                sqlBuilder.setLength(0);
                                store.executeSql(sql);
                            }
                        }

                        reader.close();
                        input.close();
                    }catch (Exception e){
                        HiLog.info(LABEL,e.getMessage());
                    }

                }
                @Override
                public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {}
            }, null);


        } catch (Exception e) {
            e.printStackTrace();
            HiLog.info(LABEL,e.getMessage());
        }
    }

    // 判断 tablename 表是否已经存在
    public boolean isTableExists(String tableName) {
        if (store == null) return false;

        String sql = "SELECT * FROM sqlite_master WHERE type='table' AND name=?";
        ResultSet resultSet = store.querySql(sql, new String[]{tableName});

        boolean exists = resultSet.goToFirstRow();
        resultSet.close();
        return exists;
    }
}

其中,resources/rawfile下应该存放着xxx.sql文件,包含着建立数据表的SQL语句。在使用时可以直接:

java 复制代码
public class MyApplication extends AbilityPackage {
    @Override
    public void onInitialize() {
        super.onInitialize();
        HiLog.info(LABEL,"MyApplication初始化");
        try {
            // 数据库初始化
            DatabaseManager.getInstance(this).initDatabaseIfNeeded();
        }catch (Exception e){
            HiLog.info(LABEL,e.getMessage());
        }

    }
}

RdbUtils

在建立数据表后,可以通过RdbStore+RdbPredicates实现对数据表的增删改查,该行为也可以封装为通用类RdbUtils。直接上代码:

java 复制代码
public class RdbUtils {

    /**
     * 通用查询方法
     * @param store RdbStore实例
     * @param predicates 查询条件(包含表名)
     * @param columns 需要查询的列(null代表全查)
     * @param clazz 目标实体的Class类型
     * @return 结果集列表
     */
    public static <T> List<T> query(RdbStore store, RdbPredicates predicates, String[] columns,Class<T> clazz) {
        List<T> list = new ArrayList<>();
        ResultSet resultSet = store.query(predicates, columns);
        try {
            while (resultSet.goToNextRow()) {
                T entity = clazz.newInstance();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    if (field.isAnnotationPresent(DbIgnore.class)) {
                        continue;
                    }
                    String columnName = getTargetColumnName(field);
                    int columnIndex = resultSet.getColumnIndexForName(columnName);
                    if (columnIndex != -1) {
                        assignFieldValue(entity, field, resultSet, columnIndex);
                    }
                }
                list.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            resultSet.close();
        }
        return list;
    }

    /**
     * 通用插入方法
     * @param store RdbStore实例
     * @param tableName 表名
     * @param object 要插入的对象
     * @return 插入行的ID 失败则返回-1
     */
    public static <T> long insert(RdbStore store, String tableName, T object) {
        ValuesBucket values = objectToValuesBucket(object);
        return store.insert(tableName, values);
    }

    /**
     * 通用删除方法
     */
    public static int delete(RdbStore store, RdbPredicates predicates) {
        return store.delete(predicates);
    }

    /**
     * 通用更新方法
     */
    public static <T> int update(RdbStore store, T object, RdbPredicates predicates) {
        ValuesBucket values = objectToValuesBucket(object);
        return store.update(values, predicates);
    }

    /**
     * 获取列名:优先从注解读取,没有注解则取字段名
     */
    private static String getTargetColumnName(Field field) {
        DbField dbField = field.getAnnotation(DbField.class);
        if (dbField != null) {
            return dbField.value(); // 返回注解中定义的 form_name
        }
        return field.getName(); // 返回字段名 formName
    }

    // --- 内部辅助方法:处理反射赋值 ---
    private static void assignFieldValue(Object entity, Field field, ResultSet resultSet, int index) throws IllegalAccessException {
        Class<?> type = field.getType();
        if (type == String.class) {
            field.set(entity, resultSet.getString(index));
        } else if (type == int.class || type == Integer.class) {
            field.set(entity, resultSet.getInt(index));
        } else if (type == long.class || type == Long.class) {
            field.set(entity, resultSet.getLong(index));
        } else if (type == double.class || type == Double.class) {
            field.set(entity, resultSet.getDouble(index));
        } else if (type == byte[].class) {
            field.set(entity, resultSet.getBlob(index));
        }
    }

    // --- 内部辅助方法:对象转ValuesBucket ---
    private static <T> ValuesBucket objectToValuesBucket(T obj) {
        ValuesBucket values = new ValuesBucket();
        Field[] fields = obj.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(DbIgnore.class)) {
                continue;
            }
            try {
                Object val = field.get(obj);
                if (val == null) {
                    continue;
                }
                String columnName = getTargetColumnName(field);
                if (val instanceof String) {
                    values.putString(columnName, (String) val);
                } else if (val instanceof Integer) {
                    values.putInteger(columnName, (Integer) val);
                } else if (val instanceof Long) {
                    values.putLong(columnName, (Long) val);
                } else if (val instanceof Double) {
                    values.putDouble(columnName, (Double) val);
                } else if (val instanceof Boolean) {
                    values.putBoolean(columnName, (Boolean) val);
                } else if (val instanceof byte[]) {
                    values.putByteArray(columnName, (byte[]) val);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return values;
    }
}

有时候,SQL使用_对字段进行命名,例如goods_id,但是PO类通常使用驼峰命名法,例如goodsId,为实现字段之间的对应关系,可以编写注解。

java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbIgnore {
}
java 复制代码
// 指定该注解用于字段(FIELD)
@Target(ElementType.FIELD)
// 指定该注解在运行时有效,这样反射才能读取到
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
    String value(); // 用来存放数据库中的列名
}

在使用时,在实体类对应字段上添加对应的数据表字段名称。例如:

java 复制代码
public class Goods<T> {
    @DbField("id")
    private Integer id;
    @DbField("goods_id")
    private Integer goodsId;
}

在需要对数据表进行操作时,可以直接:

java 复制代码
public List<User> userLogin(String userName, String password){
    dbManager = DatabaseManager.getInstance(this);
    rdbStore = dbManager.getStore();
    RdbPredicates rdbPredicates = new RdbPredicates("user");
    rdbPredicates.equalTo("no",userName);
    rdbPredicates.equalTo("password",password);
    return RdbUtils.query(rdbStore,rdbPredicates,null,User.class);
}
相关推荐
Decay__1 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
小徐不徐说6 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
论文复现现场6 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
Decay__7 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
心梦缘-雪雁9 小时前
SQL Server 查询数据存在哪个表里
数据库
lusklusklusk9 小时前
Python 巡检脚本:进程运行状态、数据库写入时效检查及邮件通知与告警
数据库·python·oracle
GeW10 小时前
产业新优势基础软件的人才底盘,从Linux与数据库开始
linux·数据库
SelectDB10 小时前
Doris Arrow Flight SQL 高速读取协议实战:与 Snowflake 客户端取数链路对照
大数据·数据库·数据分析
曼岛_10 小时前
[图解密码学]第4章 古典密码
java·数据库·密码学
瀚高PG实验室11 小时前
HGDB V9.0.5 .pgpass 免密登录提示 IsEncryptNon-base64 encoding 警告处理
数据库·postgresql·瀚高数据库