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);
}
相关推荐
程序员-Benothing1 小时前
MySQL 中的事务隔离级别有哪些?默认的事务隔离级别是什么?为什么选择这个级别?
数据库·mysql
lv__pf2 小时前
【TL mysql 3】
数据库
xiaomici3 小时前
Datasphere的数据merge
数据库
小林ixn3 小时前
从零设计一个博客系统的数据库:表结构、索引与约束的实战思考
数据库·后端·mysql
Yang96113 小时前
一台顶三台:鼎讯DLJ-1在不同故障类型中的模式切换数据复盘
服务器·网络·数据库
潘正翔3 小时前
Memcached构建缓存服务器
运维·服务器·数据库·缓存·云原生·memcached
NineData4 小时前
NineData 与平凯数据库(TiDB 企业版)完成产品兼容性互认证
数据库·tidb·ninedata·玖章算术·平凯数据库·产品兼容性认证·tidb企业版
咖啡星人k4 小时前
2026 Text-to-SQL:一句话让 AI 写 SQL,自然语言直接查数据库(MonkeyCode 云端实战)
数据库·机器学习·语言模型·自然语言处理
GreatSQL社区4 小时前
少敲一个字母,GreatSQL 对所有查询“已读不回”
数据库·mysql·greatsql