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);
}
相关推荐
程序员JerrySUN14 分钟前
Jetson Edge AI 实战01:Nano、Xavier、Orin 怎么选?Jetson 硬件选型详解【视频讲解】
java·数据库·redis·安全·mybatis
oradh40 分钟前
Oracle Undo问题总结(ORA-600 [4xxx] 系列错误)
数据库·oracle
达梦数据43 分钟前
DMDRS辅助表生成规则与作用
数据库·oracle
‎ദ്ദിᵔ.˛.ᵔ₎1 小时前
MySQL 表约束
数据库·mysql
Doris__HE1 小时前
【元脑服务器NF5476G7-NF5476M7技术规格分享】
运维·服务器·网络·数据库·性能优化
笃行3504 小时前
别拿MySQL数据库管理工具硬导:MySQL 迁 KingbaseES 的完整工具链
数据库
风哥2号4 小时前
数据库教程FGMT51‑PostgreSQL实例管理与参数文件
数据库·postgresql
蓝速科技4 小时前
桌面双屏翻译机量产调试:三类场景兼容性破局方案丨蓝速科技
运维·数据库·人工智能·科技·技术分享
达梦数据5 小时前
DMDRS空间数据类型说明:Oracle、DM8、MySQL空间数据类型映射
数据库·mysql·oracle
白远山5 小时前
24小时自助健身房系统开发实战:从需求分析到完整指南
java·开发语言·数据库·数据挖掘·需求分析