硬件设备:鸿蒙平板电脑
开发环境: 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);
}