在鸿蒙中应用数据可以在本地单机存储,也支持分布式的跨设备相互同步的方式实现数据持久化。本地单机持久化有关系型数据库、对象关系映射数据库和轻量级偏好数据库。分布式持久化有分布式数据服务。下面进行详细介绍。
鸿蒙的关系型数据库是基于 SQLite 的,它运行所需的内存极小。鸿蒙提供的数据库功能更加完善,查询效率更高。对外提供了一系列的增、删、改、查接口,也可以直接运行SQL语句。
基本概念
- 关系型数据库
创建在关系模型基础上的数据库,以行和列的形式存储数据。
- 谓词
数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
- 结果集
指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便的拿到用户想要的数据。
关系型数据库的使用,首先创建一个 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(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;
}
}
配置数据库版本和实体
@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();