在鸿蒙中应用数据可以在本地单机存储,也支持分布式的跨设备相互同步的方式实现数据持久化。本地单机持久化有关系型数据库对象关系映射数据库轻量级偏好数据库。分布式持久化有分布式数据服务。下面进行详细介绍。

一、关系型数据库

        鸿蒙的关系型数据库是基于 SQLite 的,它运行所需的内存极小。鸿蒙提供的数据库功能更加完善,查询效率更高。对外提供了一系列的增、删、改、查接口,也可以直接运行SQL语句。

基本概念

  • 关系型数据库

    创建在关系模型基础上的数据库,以行和列的形式存储数据。

  • 谓词

    数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。

  • 结果集

    指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便的拿到用户想要的数据。

创建DataAbility

       关系型数据库的使用,首先创建一个 DataAbility 命名为 AppDatabaseDataAbility(创建方法之前的文章有写)。或者直接复制以下代码。

public class AppDatabaseDataAbility extends Ability {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        HiLog.info(LABEL_LOG, "DatabaseDataAbility onStart");
    }

    @Override
    public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
        return null;
    }

    @Override
    public int insert(Uri uri, ValuesBucket value) {
        HiLog.info(LABEL_LOG, "DatabaseDataAbility insert");
        return 999;
    }

    @Override
    public int delete(Uri uri, DataAbilityPredicates predicates) {
        return 0;
    }

    @Override
    public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
        return 0;
    }

    @Override
    public FileDescriptor openFile(Uri uri, String mode) {
        return null;
    }

    @Override
    public String[] getFileTypes(Uri uri, String mimeTypeFilter) {
        return new String[0];
    }

    @Override
    public PacMap call(String method, String arg, PacMap extras) {
        return null;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }
}

如果是手动创建,需要将 DataAbility  添加到配置文件中。

"abilities": [
    "abilities": [
        {
            "permissions": [
                "com.example.helloharmony.db.DataAbilityShellProvider.PROVIDER"
            ],
            "name": "com.example.helloharmony.db.AppDatabaseDataAbility",
            "icon": "$media:icon",
            "description": "描述",
            "type": "data",
            "uri": "dataability://com.example.helloharmony.db.AppDatabaseDataAbility"
        }
    ]
]

配置数据库

假如我们现在需要创建一个商品信息表,其中包含GUID、商品名称、操作人员和商品数量。

private StoreConfig storeConfig = StoreConfig.newDefaultConfig("goodsinfo.db");

private RdbStore rdbStore;

private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() {
    @Override
    public void onCreate(RdbStore rdbStore) {
        // 数据库首次创建时调用
        rdbStore.executeSql("CREATE TABLE IF NOT EXISTS goods(" +
                "guid PRIMARY KEY," +
                "name TEXT NOT NULL," +
                "operator TEXT NOT NULL," +
                "number INTEGER)");
    }

    @Override
    public void onUpgrade(RdbStore rdbStore, int i, int i1) {
        // 数据库升级时调用(版本号变更时)
    }
};

onStart 函数中初始化数据库

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    HiLog.info(LABEL_LOG, "DatabaseDataAbility onStart");

    // 创建一个数据库助手用于访问数据库
    DatabaseHelper databaseHelper = new DatabaseHelper(this);
    // 获取RDB存储
    rdbStore = databaseHelper.getRdbStore(storeConfig, 1, rdbOpenCallback, null);
}

重写数据操作方法 

 重写 DataAbility 框架的查询、新增、删除和更新方法以便操作数据库(可以自定义方法)

query() 方法

/**
 * 数据库查询
 *
 * @param uri        数据的数据库表
 * @param columns    要查询的列
 * @param predicates 过滤条件。如果该参数为空,则默认查询所有数据记录
 * @return 查询的数据
 */
@Override
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
    RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, "goods");

    return rdbStore.query(rdbPredicates, columns);
}

insert() 方法 

/**
 * 插入数据
 *
 * @param uri   数据的数据库表
 * @param value 要插入表中的数据行
 * @return 行ID
 */
@Override
public int insert(Uri uri, ValuesBucket value) {
    HiLog.info(LABEL_LOG, "AppDatabaseDataAbility insert");

    String path = uri.getLastPath();
    if ("goods".equals(path)) {
        ValuesBucket values = new ValuesBucket();
        values.putString("guid", value.getString("guid"));
        values.putString("name", value.getString("name"));
        values.putString("operator", value.getString("operator"));
        values.putInteger("number", value.getInteger("number"));

        // 行ID
        int index = (int) rdbStore.insert("goods", values);
        // 插入成功时知该表格数据的订阅者
        DataAbilityHelper.creator(this, uri).notifyChange(uri);
        return index;
    }

    HiLog.info(LABEL_LOG, "DataAbility insert path is not matched");
    return -1;
}

delete() 函数

/**
 * 删除数据
 *
 * @param uri        数据的数据库表
 * @param predicates 过滤条件。如果该参数为空,则默认查询所有数据记录
 * @return 受影响行ID
 */
@Override
public int delete(Uri uri, DataAbilityPredicates predicates) {
    RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, "goods");
    // 受影响行ID
    int index = rdbStore.delete(rdbPredicates);
    // 删除成功时知该表格数据的订阅者
    DataAbilityHelper.creator(this, uri).notifyChange(uri);
    return index;
}

updata() 函数

/**
 * 更新数据
 *
 * @param uri        数据的数据库表
 * @param value      要插入表中的数据行
 * @param predicates 过滤条件。如果该参数为空,则默认查询所有数据记录
 * @return 受影响行ID
 */
@Override
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
    RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, "goods");
    int index = rdbStore.update(value, rdbPredicates);
    // 数据变更时知该表格数据的订阅者
    DataAbilityHelper.creator(this, uri).notifyChange(uri);
    return index;
}

关系数据库的使用

查询数据库

private void query() {
    String[] columns = new String[]{"guid", "name", "number"};
    // 构造查询条件,假如我们要查询商品为可乐的数量在0到200之间的操作有哪些?
    DataAbilityPredicates predicates = new DataAbilityPredicates();
    // 这部分的含义是商品名称为可乐的
    predicates.equalTo("name", "可乐");
    // 这部分的含义是商品数量在0到200之间的
    predicates.between("number", 0, 200);

    try {
        ResultSet resultSet = databaseHelper.query(
                Uri.parse("dataability:///com.example.helloharmony.db.AppDatabaseDataAbility" + "/goods"),
                columns,
                predicates);

        if (resultSet.goToFirstRow()) {
            do {
                String name = resultSet.getString(resultSet.getColumnIndexForName("name"));
                String operator = resultSet.getString(resultSet.getColumnIndexForName("operator"));
                int number = resultSet.getInt(resultSet.getColumnIndexForName("number"));
                HiLog.info(LABEL_LOG, "商品:" + name + " 操作员:" + operator + " 数量:" + number);
            } while (resultSet.goToNextRow());
        }
    } catch (DataAbilityRemoteException e) {
        e.printStackTrace();
    }
}

二、对象关系映射数据库 

        对象关系映射数据库是在 SQLite 上做了一层封装,屏蔽了底层数据库的SQL操作,提供一系列的面向对象接口,而不必再去编写复杂是SQL语句。

基本概念

  • 对象关系映射数据库的三个主要组件
    • 数据库:被开发者用@Database注解,且继承了OrmDatabase的类,对应关系型数据库。
    • 实体对象:被开发者用@Entity注解,且继承了OrmObject的类,对应关系型数据库中的表。
    • 对象数据操作接口:包括数据库操作的入口OrmContext类和谓词接口(OrmPredicate)等。
  • 谓词

    数据库中是用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。对象关系映射数据库将SQLite数据库中的谓词封装成了接口方法供开发者调用。开发者通过对象数据操作接口,可以访问到应用持久化的关系型数据。

  • 对象关系映射数据库

    通过将实例对象映射到关系上,实现使用操作实例对象的语法,来操作关系型数据库。它是在SQLite数据库的基础上提供的一个抽象层。

创建对象关系映射数据库

首先在配置“build.gradle”文件中添加下面的模块启动注解编译

ohos {
    ...
    compileOptions{
        annotationEnabled true
    }
    ...
}

我们还是以人员信息为例:

创建数据库实体Entity

@Entity(tableName = "person_info")
public class PersonEntity extends OrmObject {

    // 指定数据库主键
    @PrimaryKey
    private String guid;

    private String name;
    private String gender;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

创建构造数据库AppDatabase

配置数据库版本和实体

@Database(version = 1, entities = {PersonEntity.class})
public abstract class AppDatabase extends OrmDatabase {
}

 对象关系映射数据库的使用

初始化

// 数据库助手
DatabaseHelper databaseHelper = new DatabaseHelper(context);
/*
* 获取对象关系映射数据库上下文
* 参数分别为:ORM数据库别名、数据库文件、ORM数据库
*/
OrmContext ormContext = databaseHelper.getOrmContext("AppDatabase", "AppDatabase.db", AppDatabase.class);

 插入数据

/**
 * 插入数据
 *
 * @param personEntity 人员信息
 * @return 操作结果
 */
 private boolean insert(PersonEntity personEntity) {
    if (ormContext.insert(personEntity)) {
        return ormContext.flush();
    }

    return false;
 }

删除数据

/**
 * 删除数据
 *
 * @param personEntity 人员信息
 * @return 操作结果
 */
private boolean delete(PersonEntity personEntity) {
    if (ormContext.delete(personEntity)) {
        return ormContext.flush();
    }

    return false;
}

更新数据

/**
 * 更新数据
 *
 * @param personEntity 人员信息
 * @return 操作结果
 */
private boolean upData(PersonEntity personEntity) {
   if (ormContext.update(personEntity)) {
        return ormContext.flush();
    }

    return false;
}

三、轻量级偏好数据库

轻量级偏好数据库主要提供轻量级Key-Value操作,支持本地应用存储少量数据,数据存储在本地文件中,同时也加载在内存中的,所以访问速度更快,效率更高。轻量级偏好数据库属于非关系型数据库,不宜存储大量数据,经常用于操作键值对形式数据的场景。

基本概念

  • Key-Value数据库

    一种以键值对存储数据的一种数据库,类似Java中的map。Key是关键字,Value是值。

  • 非关系型数据库

    区别于关系数据库,不保证遵循ACID(Atomic、Consistency、Isolation及Durability)特性,不采用关系模型来组织数据,数据之间无关系,扩展性好。

  • 偏好数据

    用户经常访问和使用的数据。

 初始化

DatabaseHelper databaseHelper = new DatabaseHelper(context);
// 通过文件名称获取偏好数据库
Preferences preferences = databaseHelper.getPreferences("Setting");

读值

// 获取键为 intKey 的值,第二个参数是如果返回的值不是 int 型返回的值。
int value = preferences.getInt("intKey", 0);

 写值

preferences.putInt("intKey", 3);
// 同步
preferences.flushSync();

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐